2023-09-19 01:22:15 -07:00
#import "abbreviations.typ"
2024-11-07 21:10:08 -08:00
// ╭─────────────────────╮
// │ Interlinear glosses │
// ╰─────────────────────╯
2023-03-23 21:34:17 -07:00
2024-08-19 15:22:39 -07:00
#let build-gloss(item-spacing, formatters, gloss-line-lists) = {
assert(gloss-line-lists.len() > 0, message: "Gloss line lists cannot be empty")
2023-03-23 21:34:17 -07:00
2024-08-19 15:22:39 -07:00
let len = gloss-line-lists.at(0).len()
2023-03-23 21:34:17 -07:00
2024-08-19 15:22:39 -07:00
for line in gloss-line-lists {
2023-03-23 21:34:17 -07:00
assert(line.len() == len)
}
2024-08-19 15:22:39 -07:00
assert(formatters.len() == gloss-line-lists.len(), message: "The number of formatters and the number of gloss line lists should be equal")
2023-03-23 21:34:17 -07:00
2024-11-07 21:10:08 -08:00
let make-item-box(..args) = {
2023-03-23 21:34:17 -07:00
box(stack(dir: ttb, spacing: 0.5em, ..args))
}
2024-11-07 21:10:08 -08:00
for item-index in range(0, len) {
2023-03-23 21:34:17 -07:00
let args = ()
2024-11-07 21:10:08 -08:00
for (line-idx, formatter) in formatters.enumerate() {
let formatter-fn = if formatter == none {
2023-03-23 21:34:17 -07:00
(x) => x
} else {
formatter
}
2024-11-07 21:10:08 -08:00
let item = gloss-line-lists.at(line-idx).at(item-index)
args.push(formatter-fn(item))
2023-03-23 21:34:17 -07:00
}
2024-11-07 21:10:08 -08:00
make-item-box(..args)
2023-09-19 03:23:50 -07:00
h(item-spacing)
2023-03-23 21:34:17 -07:00
}
}
2024-11-07 21:10:08 -08:00
// Typesets the internal part of the interlinear glosses. This function does not deal with the external matters of numbering and labelling; which are handled by `example()`.
2023-03-23 21:34:17 -07:00
#let gloss(
2024-11-07 21:10:08 -08:00
header: none,
header-style: none,
source: (),
source-style: none,
transliteration: none,
transliteration-style: none,
morphemes: none,
morphemes-style: none,
additional-lines: (), //List of list of content
translation: none,
translation-style: none,
item-spacing: 1em,
2023-03-23 21:34:17 -07:00
) = {
2024-11-07 21:10:08 -08:00
assert(type(source) == "array", message: "source needs to be an array; perhaps you forgot to type `(` and `)`, or a trailing comma?")
if morphemes != none {
assert(type(morphemes) == "array", message: "morphemes needs to be an array; perhaps you forgot to type `(` and `)`, or a trailing comma?")
assert(source.len() == morphemes.len(), message: "source and morphemes have different lengths")
}
if transliteration != none {
assert(transliteration.len() == source.len(), message: "source and transliteration have different lengths")
}
let gloss-items = {
if header != none {
if header-style != none {
header-style(header)
} else {
header
}
linebreak()
}
2023-03-23 21:34:17 -07:00
2024-11-07 21:10:08 -08:00
let formatters = (source-style,)
let gloss-line-lists = (source,)
2023-03-23 21:34:17 -07:00
2024-11-07 21:10:08 -08:00
if transliteration != none {
formatters.push(transliteration-style)
gloss-line-lists.push(transliteration)
2023-07-31 02:17:09 -07:00
}
2023-03-23 21:34:17 -07:00
2024-11-07 21:10:08 -08:00
if morphemes != none {
formatters.push(morphemes-style)
gloss-line-lists.push(morphemes)
2023-03-23 21:34:17 -07:00
}
2024-11-07 21:10:08 -08:00
for additional in additional-lines {
formatters.push(none) //TODO fix this
gloss-line-lists.push(additional)
}
2023-03-23 21:34:17 -07:00
2024-11-07 21:10:08 -08:00
build-gloss(item-spacing, formatters, gloss-line-lists)
2023-03-23 21:34:17 -07:00
2024-11-07 21:10:08 -08:00
if translation != none {
linebreak()
2023-03-23 21:34:17 -07:00
2024-11-07 21:10:08 -08:00
if translation-style == none {
translation
} else {
translation-style(translation)
}
}
}
2023-03-23 21:34:17 -07:00
2024-11-07 21:10:08 -08:00
align(left)[#gloss-items]
}
2023-03-23 21:34:17 -07:00
2024-11-07 21:10:08 -08:00
// ╭─────────────────────╮
// │ Linguistic examples │
// ╰─────────────────────╯
2023-03-23 21:34:17 -07:00
2024-11-07 21:10:08 -08:00
#let example-count = counter("example-count")
2023-07-04 01:52:43 -07:00
2024-11-07 21:10:08 -08:00
#let example(
label: none,
label-supplement: [example],
gloss-padding: 2.5em, //TODO document these
left-padding: 0.5em,
numbering: false,
breakable: false,
..args
) = {
let add-subexample(subexample, count) = {
// Remove parameters which are not used in the `gloss`.
// TODO Make this functional, if (or when) it’ s possible in Typst: filter out `label` and `label-supplement` when they are passed below.
let subexample-internal = subexample
if "label" in subexample-internal {
let _ = subexample-internal.remove("label")
}
if "label-supplement" in subexample-internal {
let _ = subexample-internal.remove("label-supplement")
2023-03-23 21:34:17 -07:00
}
2024-11-07 21:10:08 -08:00
par()[
#box()[
#figure(
kind: "subexample",
numbering: it => [#example-count.display()#count.display("a")],
outlined: false,
supplement: it => {if "label-supplement" in subexample {return subexample.label-supplement} else {return "example"}},
stack(
dir: ltr, //TODO this needs to be more flexible
[(#context count.display("a"))],
left-padding,
gloss(..subexample-internal)
)
2024-11-09 00:05:27 -08:00
) #if "label" in subexample {std.label(subexample.label)}
2024-11-07 21:10:08 -08:00
]
]
2023-03-23 21:34:17 -07:00
}
2023-04-05 07:59:29 -07:00
if numbering {
2024-11-07 21:10:08 -08:00
example-count.step()
2023-04-05 07:59:29 -07:00
}
2023-07-04 01:41:26 -07:00
2024-11-07 21:10:08 -08:00
let example-number = if numbering {
[(#context example-count.display())]
2023-03-23 21:34:17 -07:00
} else {
none
}
2023-07-03 16:32:58 -07:00
style(styles => {
block(breakable: breakable)[
2024-08-17 09:21:54 -07:00
#figure(
2024-11-07 21:10:08 -08:00
kind: "example",
numbering: it => [#example-count.display()],
outlined: false,
2024-08-18 22:25:26 -07:00
supplement: label-supplement,
2024-08-17 09:21:54 -07:00
stack(
dir: ltr, //TODO this needs to be more flexible
2024-08-19 15:22:39 -07:00
left-padding,
2024-11-07 21:10:08 -08:00
[#example-number],
2024-11-09 00:00:12 -08:00
gloss-padding - left-padding - measure([#example-number]).width,
2024-11-07 21:10:08 -08:00
{
if args.pos().len() == 1 { // a simple example with no sub-examples
gloss(..arguments(..args.pos().at(0)))
}
else { // containing sub-examples
let subexample-count = counter("subexample-count")
subexample-count.update(0)
set align(left)
if "header" in args.named() {
par[#args.named().header]
}
for subexample in args.pos() {
subexample-count.step()
add-subexample(subexample, subexample-count)
}
}
}
2024-08-17 09:21:54 -07:00
),
2024-11-09 00:05:27 -08:00
) #if label != none {std.label(label)}
2023-07-03 16:32:58 -07:00
]
}
)
2023-03-23 21:34:17 -07:00
}
2024-11-07 21:10:08 -08:00
#let numbered-example = example.with(numbering: true)