I would like to have a function foo("bar") that I could use to designate a given location in my document (= where foo() is called) as the target of internal references that would automatically link each occurrence of “bar” in the whole document to that location.
As an example, the following source:
= A definition of bar #foo("bar")
Here is what bar is about.
= A definition of baz #foo("baz")
Here is what baz is about.
= Another section
Let's talk more about bar. Reading about bar is super interesting. But bar has nothing to do with baz.
should produce a document where all occurrences of “bar” point to the first section, and those of “baz” to the second section.
I would like to avoid any special function calls in the main text. Another nice feature would be the ability to match upper- and lowercase versions of the word, and an additional command (e.g., nofoo()) that could be used to temporarily prevent automatic linking.
#let no-link-state = state("no-link", false)
#let no-link(body) = {
no-link-state.update(true)
body
no-link-state.update(false)
}
#let anchor(..terms) = {
terms.pos().fold(none, (acc, term) => {
[#metadata(("anchor", term))#label(term)]
acc
})
}
#let link-anchor(case-sensitive: false, body) = context {
let all-metadata = query(selector(metadata).after(here()))
let anchor-metadata = all-metadata.filter(it => it.value.first() == "anchor")
let anchor-words = anchor-metadata.map(it => it.value.slice(1,)).flatten().dedup()
anchor-words.fold(body, (acc, word) => {
if case-sensitive {
show regex("\\b" + word + "\\b"): it => context {
if no-link-state.get() { word } else { link(label(word), it) }
}
acc
} else {
show regex("(?i)\\b" + word + "\\b"): it => context {
if no-link-state.get() { word } else { link(label(word), it) }
}
acc
}
})
}
#show: link-anchor.with(case-sensitive: true)
= A definition of bar #anchor("bar")
Here is what bar is about.
= A definition of baz #anchor("baz")
Here is what baz is about.
= Another section
Let's talk more about bar. Reading about bar is super interesting. But bar has nothing to do with baz.
#pagebreak()
#no-link[baz] baz BAz bar
I’ve found that it’s usually better to use function calls than do string/regex matching or weird hacks.