78 lines
2.7 KiB
Typst
78 lines
2.7 KiB
Typst
#let run-in-join(
|
|
// Determines the in-between separator for all but the last item.
|
|
// - If set to "auto", we'll detect whether content has commas.
|
|
// - Otherwise, use it literally (e.g., ", ", " / ", etc.).
|
|
separator: "auto",
|
|
|
|
// The word or phrase (e.g., "and", "or", "and/or") that comes before the last item.
|
|
// - If set to `none`, we skip any coordinator.
|
|
coordinator: "and",
|
|
|
|
// Content items you want to join.
|
|
// e.g., `[ [#(1) " item one"], [#(2) " item two"], ... ]`
|
|
..content,
|
|
) = {
|
|
// 1. Decide on the base separator.
|
|
let sep = if separator == "auto" {
|
|
// naive check: if any item in the content sequence includes a comma, use semicolons
|
|
if content.pos().any((item) => repr(item).find(regex("\[[^]]*,[^]]*\]")) != none) {
|
|
"; "
|
|
} else {
|
|
", "
|
|
}
|
|
} else {
|
|
separator
|
|
}
|
|
|
|
// 2. Figure out how to handle the last separator.
|
|
// If `coordinator` is none, just do a normal join.
|
|
// If `oxford` is true and there are 3+ items, insert a comma before coordinator.
|
|
let final-sep = if coordinator != none and content.pos().len() == 2 {
|
|
// For 3 or more items, we do something like: ", and "
|
|
// That is, a final comma plus the coordinator
|
|
" " + coordinator + " "
|
|
} else if coordinator != none and content.pos().len() > 2 {
|
|
// For 3 or more items, we do something like: ", and "
|
|
// That is, a final comma plus the coordinator
|
|
sep + coordinator + " "
|
|
} else {
|
|
// No coordinator or too few items; the final separator is just `sep`.
|
|
sep
|
|
}
|
|
|
|
// 3. Perform the join:
|
|
content.pos().join(
|
|
sep,
|
|
last: final-sep
|
|
)
|
|
}
|
|
|
|
#let run-in-enum(separator: "auto", coordinator: "and", numbering-pattern: "(1)", numbering-formatter: (it) => [#it], ..items) = {
|
|
let contents = items.pos().enumerate(start: 1).map(((index, item)) => {
|
|
[#numbering-formatter(numbering(numbering-pattern, index))~#item]
|
|
})
|
|
run-in-join(separator: separator, coordinator: coordinator, ..contents)
|
|
}
|
|
|
|
#let run-in-list(separator: "auto", coordinator: "and", marker: [•], ..items) = {
|
|
let contents = items.pos().map((item) => {
|
|
[#marker~#item]
|
|
})
|
|
run-in-join(separator: separator, coordinator: coordinator, ..contents)
|
|
}
|
|
|
|
#let run-in-terms(separator: "auto", coordinator: "and", term-formatter: (it) => [#strong(it)], ..terms) = {
|
|
let contents = terms.pos().map((t) => {
|
|
if type(t) != array or t.len() != 2 {
|
|
panic("arguments to run-in-terms must be arrays of the form (term, definition)")
|
|
}
|
|
[#term-formatter(t.at(0)):~#t.at(1)]
|
|
})
|
|
run-in-join(separator: separator, coordinator: coordinator, ..contents)
|
|
}
|
|
|
|
#let run-in-verse(separator: [ /~], coordinator: none, ..verses) = {
|
|
let contents = verses
|
|
run-in-join(separator: separator, coordinator: coordinator, ..contents)
|
|
}
|