Skip to content

Commit 6af4171

Browse files
committed
Correct handling of crossrefs
Fixes #12 again :) This extracts crossref keys before sending the content to citeproc, then restores them for Quarto to process. It handles all of Quarto's built-in crossref types *and* custom crossref types. ✨magic✨
1 parent d991ec8 commit 6af4171

File tree

10 files changed

+149
-24
lines changed

10 files changed

+149
-24
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,16 +185,16 @@ format:
185185
html: # Regular built-in format
186186
citeproc: false
187187
filters:
188-
- at: post-quarto
188+
- at: pre-quarto
189189
path: _extensions/andrewheiss/wordcount/citeproc.lua
190-
- at: post-quarto
190+
- at: pre-quarto
191191
path: _extensions/andrewheiss/wordcount/wordcount.lua
192192
jss-pdf: # Custom third-party format
193193
citeproc: false
194194
filters:
195-
- at: post-quarto
195+
- at: pre-quarto
196196
path: _extensions/andrewheiss/wordcount/citeproc.lua
197-
- at: post-quarto
197+
- at: pre-quarto
198198
path: _extensions/andrewheiss/wordcount/wordcount.lua
199199
```
200200

@@ -485,9 +485,9 @@ format:
485485
html:
486486
citeproc: false
487487
filters:
488-
- at: post-quarto
488+
- at: pre-quarto
489489
path: "path/to/citeproc.lua"
490-
- at: post-quarto
490+
- at: pre-quarto
491491
path: "path/to/wordcount.lua"
492492
```
493493

README.qmd

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,16 @@ format:
123123
html: # Regular built-in format
124124
citeproc: false
125125
filters:
126-
- at: post-quarto
126+
- at: pre-quarto
127127
path: _extensions/andrewheiss/wordcount/citeproc.lua
128-
- at: post-quarto
128+
- at: pre-quarto
129129
path: _extensions/andrewheiss/wordcount/wordcount.lua
130130
jss-pdf: # Custom third-party format
131131
citeproc: false
132132
filters:
133-
- at: post-quarto
133+
- at: pre-quarto
134134
path: _extensions/andrewheiss/wordcount/citeproc.lua
135-
- at: post-quarto
135+
- at: pre-quarto
136136
path: _extensions/andrewheiss/wordcount/wordcount.lua
137137
```
138138

@@ -361,9 +361,9 @@ format:
361361
html:
362362
citeproc: false
363363
filters:
364-
- at: post-quarto
364+
- at: pre-quarto
365365
path: "path/to/citeproc.lua"
366-
- at: post-quarto
366+
- at: pre-quarto
367367
path: "path/to/wordcount.lua"
368368
```
369369

_extensions/wordcount/_extension.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@ authors:
44
orcid: 0000-0003-1866-860X
55
- name: Justin Landis
66
orcid: 0000-0001-5501-4934
7-
version: 1.7.0
7+
version: 1.7.1
88
quarto-required: ">=1.4.551"
99
contributes:
1010
shortcodes:
1111
- "words.lua"
1212
format:
1313
common:
1414
filters:
15-
- at: post-quarto
15+
- at: pre-quarto
1616
path: citeproc.lua
17-
- at: post-quarto
17+
- at: pre-quarto
1818
path: wordcount.lua
1919
citeproc: false
2020
html: default

_extensions/wordcount/citeproc.lua

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,68 @@
1-
-- Lua filter that behaves like `--citeproc`
1+
-- Check if a citekey looks like a crossref
2+
function is_crossref(id, meta)
3+
-- These are all of Quarto's built-in crossref prefixes:
4+
-- https://quarto.org/docs/authoring/cross-references.html#reserved-prefixes
5+
--
6+
-- This checks to see if it's one of those
7+
local known_prefixes = {
8+
"fig", "tbl", "lst", "tip", "nte", "wrn", "imp", "cau", "thm", "lem", "cor",
9+
"prp", "cnj", "def", "exm", "exr", "sol", "rem", "alg", "eq", "sec"
10+
}
11+
for _, prefix in ipairs(known_prefixes) do
12+
if id:match("^" .. prefix .. "%-") then
13+
return true
14+
end
15+
end
16+
17+
-- Users can specify their own custom crossref types:
18+
-- https://quarto.org/docs/authoring/cross-references-custom.html
19+
--
20+
-- This checks the document metadata and to see if there's a crossref entry
21+
-- and then extracts the key prefix if present
22+
if meta.crossref and meta.crossref.custom then
23+
local custom = meta.crossref.custom
24+
-- custom could be a list of tables
25+
for i = 1, #custom do
26+
local item = custom[i]
27+
if item.key then
28+
local key = pandoc.utils.stringify(item.key)
29+
if id:match("^" .. key .. "%-") then
30+
return true
31+
end
32+
end
33+
end
34+
end
35+
36+
return false
37+
end
38+
39+
-- Run citeproc, but first protect/preserve all the cross references
240
function Pandoc (doc)
3-
return pandoc.utils.citeproc(doc)
41+
-- Remove crossref keys before running citeproc
42+
local crossrefs = {}
43+
doc = doc:walk({
44+
Cite = function(el)
45+
if is_crossref(el.citations[1].id, doc.meta) then
46+
-- Store the cite element with a unique placeholder
47+
local placeholder = "CROSSREF" .. #crossrefs .. "PLACEHOLDER"
48+
table.insert(crossrefs, el)
49+
return pandoc.Str(placeholder)
50+
end
51+
end
52+
})
53+
54+
-- Run citeproc
55+
doc = pandoc.utils.citeproc(doc)
56+
57+
-- Put crossref keys back now that citations have been handled
58+
doc = doc:walk({
59+
Str = function(el)
60+
local match = el.text:match("CROSSREF(%d+)PLACEHOLDER")
61+
if match then
62+
return crossrefs[tonumber(match) + 1]
63+
end
64+
end
65+
})
66+
67+
return doc
468
end

template.qmd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ format: wordcount-html
1010
# format:
1111
# html:
1212
# filters:
13-
# - at: post-quarto
13+
# - at: pre-quarto
1414
# path: path/to/citeproc.lua
15-
# - at: post-quarto
15+
# - at: pre-quarto
1616
# path: path/to/wordcount.lua
1717
# citeproc: false
1818

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
test_that("counting with note-based citation works with custom cross references", {
2+
test_file <- test_file_parts(here::here("tests/testthat/test-notes-citation-crossref-custom.qmd"))
3+
csl_file <- test_file_parts(here::here("tests/testthat/csl/chicago-notes-bibliography.csl"))
4+
5+
create_local_quarto_project(test_file = test_file, csl_file = csl_file)
6+
7+
quarto::quarto_render(input = test_file$qmd, quiet = TRUE)
8+
9+
counts <- get_wordcounts(test_file$md)
10+
11+
expect_equal(counts$wordcount_appendix_words, 0)
12+
expect_equal(counts$wordcount_body_words, 15)
13+
expect_equal(counts$wordcount_note_words, 34)
14+
expect_equal(counts$wordcount_ref_words, 34)
15+
expect_equal(counts$wordcount_total_words, 83)
16+
})
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
title: Body + references + note-based citations + custom crossrefs
3+
format: wordcount-markdown
4+
csl: chicago-notes-bibliography.csl
5+
6+
crossref:
7+
custom:
8+
- kind: float
9+
reference-prefix: Thing
10+
key: thing
11+
12+
references:
13+
- id: Lovelace1842
14+
author:
15+
- family: Lovelace
16+
given: Ada Augusta
17+
citation-key: Lovelace1842
18+
container-title: Taylor's Scientific Memoirs
19+
issued:
20+
- year: 1842
21+
language: en-GB
22+
page: 666–731
23+
title: >-
24+
Sketch of the analytical engine invented by Charles Babbage, by LF Menabrea,
25+
officer of the military engineers, with notes upon the memoir by the
26+
translator
27+
type: article-journal
28+
volume: 3
29+
---
30+
31+
Here are four words [@Lovelace1842]. See @tbl-example for more.
32+
33+
| a | b |
34+
|---|---|
35+
| 1 | 2 |
36+
37+
: Three words here {#tbl-example}
38+
39+
See @thing-example:
40+
41+
::: {#thing-example}
42+
43+
Words here.
44+
45+
:::

tests/testthat/test-notes-citation-crossref.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ test_that("counting with note-based citation works with cross references", {
99
counts <- get_wordcounts(test_file$md)
1010

1111
expect_equal(counts$wordcount_appendix_words, 0)
12-
expect_equal(counts$wordcount_body_words, 13)
12+
expect_equal(counts$wordcount_body_words, 11)
1313
expect_equal(counts$wordcount_note_words, 34)
1414
expect_equal(counts$wordcount_ref_words, 34)
15-
expect_equal(counts$wordcount_total_words, 81)
15+
expect_equal(counts$wordcount_total_words, 79)
1616
})

tests/testthat/test-notes-citation-crossref.qmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: Body + references
2+
title: Body + references + note-based citations + crossrefs
33
format: wordcount-markdown
44
csl: chicago-notes-bibliography.csl
55

tests/testthat/test-use-as-filter.qmd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ title: Use as filter instead of output format
44
format:
55
markdown:
66
filters:
7-
- at: post-quarto
7+
- at: pre-quarto
88
path: _extensions/wordcount/citeproc.lua
9-
- at: post-quarto
9+
- at: pre-quarto
1010
path: _extensions/wordcount/wordcount.lua
1111
citeproc: false
1212

0 commit comments

Comments
 (0)