Add sub-examples functionality (#7)

* Add sub-examples (resolves #4)

* Separate `gloss()` and `example()`

`gloss()` typesets are interlinear glosses, while `example()` treats everything extra that has to do with linguistic examples.
This commit is contained in:
Maja Abramski-Kronenberg 2024-11-08 07:10:08 +02:00 committed by Greg Shuflin
parent c7eded63d5
commit ae3784f4ec
3 changed files with 409 additions and 238 deletions

Binary file not shown.

View File

@ -1,5 +1,7 @@
#set document(title: "typst leipzig-glossing documentation")
#import "leipzig-gloss.typ": abbreviations, gloss, numbered-gloss, gloss-count
#import "leipzig-gloss.typ": abbreviations, example, example-count, gloss, numbered-example
#set heading(numbering: "1.")
#show link: x => underline[*#x*]
@ -11,14 +13,14 @@
}
block(stroke: 0.5pt + black, inset: 4pt, width: 100%, breakable: false)[
#eval(contents, mode: "markup", scope: (gloss: gloss, numbered-gloss: numbered-gloss) + addl-bindings)
#eval(contents, mode: "markup", scope: (gloss: gloss, example: example, numbered-example: numbered-example) + addl-bindings)
#block(fill: luma(230), inset: 8pt, radius: 4pt, breakable: false, width: 100%, raw(full-contents, lang: "typst"))
]
}
// Abbreviations used in this document
#import abbreviations: poss, prog, sg, pl, sbj, obj, fut, neg, obl, gen, com, ins, all, pst, inf
#import abbreviations: poss, prog, sg, pl, sbj, obj, fut, neg, obl, gen, com, ins, all, pst, inf, indf, def, dem, pred
#import abbreviations: art, dat, du, A, P, prf
#let fmnt = abbreviations.emit-abbreviation("FMNT")
@ -55,151 +57,31 @@ visit the module's Github repository
= Basic glossing functionality
As a first example, here is a gloss of a text in Georgian, along with the Typst code used to generate it:
#codeblock(
"#gloss(
header: [from \"Georgian and the Unaccusative Hypothesis\", Alice Harris, 1982],
source: ([ბავშვ-ი], [ატირდა]),
transliteration: ([bavšv-i], [aṭirda]),
morphemes: ([child-#smallcaps[nom]], [3S/cry/#smallcaps[incho]/II]),
translation: [The child burst out crying],
)", unevaled-first-line: "#import \"leipzig-gloss.typ\": gloss")
The function `gloss()` typesets _bare_ interlinear glosses (including styling, see @styling and @additional-lines). Normally when adding linguistic examples use the `example()` function, which calls `gloss()` internally and includes functionality that has to do with linguistic examples: numbering, labelling/referencing and sub-examples. `gloss()` is to be used when only the basic function of typesetting interlinear glosses is needed. Unlike `gloss()`, the function `example()` does not take the different parameters directly, but takes a list of dictionaries whose keys and values correspond to `gloss()`s parameters (with added options such as `label` and `numbering`). It also indents the text even when numbering is not enabled:
#codeblock(
"#example(
(
header: [from \"Georgian and the Unaccusative Hypothesis\", Alice Harris, 1982],
source: ([ბავშვ-ი], [ატირდა]),
transliteration: ([bavšv-i], [aṭirda]),
morphemes: ([child-#smallcaps[nom]], [3S/cry/#smallcaps[incho]/II]),
translation: [The child burst out crying],
)", unevaled-first-line: "#import \"leipzig-gloss.typ\": gloss")
)
)")
And an example for English which exhibits some additional styling, and uses imports from another file
for common glossing abbreviations:
#codeblock(
"#gloss(
source: ([I'm], [eat-ing], [your], [head]),
source-style: (item) => text(fill: red)[#item],
morphemes: ([1#sg.#sbj\=to.be], [eat-#prog], [2#sg.#poss], [head]),
morphemes-style: text.with(size: 10pt, fill: blue),
translation: text(weight: \"semibold\")[I'm eating your head!],
translation-style: (item) => [\"#item\"],
)
", addl-bindings: (poss: poss, prog: prog, sg: sg, sbj: sbj))
The `#gloss` function has three pre-defined parameters for glossing levels:
`source`, `transliteration`, and `morphemes`. It also has two parameters
for unaligned text: `header` for text that precedes the gloss, and
`translation` for text that follows the gloss.
The `morphemes` param can be skipped, if you just want to provide a source
text and translation, without a gloss:
#codeblock(
"#gloss(
source: ([Trato de entender, debo comprender, qué es lo que ha hecho conmigo],),
source-style: emph,
translation: [I try to understand, I must comprehend, what she has done with me],
)
")
Note that it is still necessary to wrap the `source` argument in an array of length one.
Here is an example of a lengthy gloss that forces a line break:
// adapted from https://brill.com/fileasset/downloads_static/static_publishingbooks_formatting_glosses_linguistic_examples.pdf
#codeblock(
"#gloss(
source: ([Ich],[arbeite],[ein],[Jahr],[um],[das],[Geld], [zu],[verdienen,],[das], [dein],[Bruder], [an],[einem],[Wochenende],[ausgibt.]),
source-style: text.with(weight: \"bold\"),
morphemes: ([I], [work],[ one], [year],[to],[the],[money],[to],[earn,], [that],[your],[brother],[on],[one], [weekend], [spends.]),
translation: [\"I work one year to earn the money that your brother spends in one weekend\"]
)", addl-bindings: (poss: poss, prog: prog, sg: sg, sbj: sbj))
To add more than three glossing lines, there is an additional parameter
`additional-lines` that can take a list of arbitrarily many more glossing
lines, which will appear below those specified in the aforementioned
parameters:
#codeblock(
"#gloss(
header: [Hunzib (van den Berg 1995:46)],
source: ([ождиг],[хо#super[н]хе],[мукъер]),
transliteration: ([oʒdig],[χõχe],[muqʼer]),
morphemes: ([ož-di-g],[xõxe],[m-uq'e-r]),
additional-lines: (
([boy-#smallcaps[obl]-#smallcaps[ad]], [tree(#smallcaps[g4])], [#smallcaps[g4]-bend-#smallcaps[pret]]),
([at boy], [tree], [bent]),
),
translation: [\"Because of the boy, the tree bent.\"]
)
")
//TODO add a custom numbering system that can handle example 18a-c of Kartvelian Morphosyntax and Number Agreement
== Numbering Glosses
The `gloss` function takes a boolean parameter `numbering` which will add an incrementing
count to each gloss. A function `numbered-gloss` is exported for convenience; this is
defined as simply `#let numbered-gloss = gloss.with(numbering: true)`, and is called with the
same arguments as `gloss`:
#codeblock(
"#gloss(
source: ([გვ-ფრცქვნ-ი],),
transliteration: ([gv-prtskvn-i],),
morphemes: ([1#pl.#obj\-peel-#fmnt],),
translation: \"You peeled us\",
numbering: true,
)
#numbered-gloss(
source: ([მ-ფრცქვნ-ი],),
transliteration: ([m-prtskvn-i],),
morphemes: ([1#sg.#obj\-peel-#fmnt],),
translation: \"You peeled me\",
)
", addl-bindings: (pl: pl, obj: obj, sg: sg, fmnt: fmnt))
The displayed number for numbered glosses is iterated for each numbered gloss
that appears throughout the document. Unnumbered glosses do not increment the
counter for the numbered glosses.
The gloss count is controlled by the Typst counter variable `gloss-count`. This
variable can be imported from the `leipzig-gloss` package and manipulated using the
standard Typst counter functions to control gloss numbering:
#codeblock(
"#gloss-count.update(20)
#numbered-gloss(
header: [from _Standard Basque: A Progressive Grammar_ by Rudolf de Rijk, quoting P. Charriton],
source: ([Bada beti guregan zorion handi baten nahia.],),
translation: [There always is in us a will for a great happiness.],
)", addl-bindings: (gloss-count: gloss-count))
References to individual examples can be achieved using the `label` argument and the referencing mechanism of Typst:
#codeblock(
"See @sorcerers:
#numbered-gloss(
header: [Middle Welsh; modified from _Grammatical number in Welsh_ (1999) by Silva Nurmio (§~2.1.1)],
source: ([ac], [ny], [allvs], [y], [dewinyon], [atteb], [idav]),
morphemes: ([and], [#neg], [be_able.#smallcaps[pret].3#sg], [#smallcaps[def]], [sorcerer.#pl], [answer.#smallcaps[inf]], [to.3#sg.#smallcaps[m]]),
translation: [and the sorcerers could not answer him],
label: \"sorcerers\",
label-supplement: [Example]
)
As we have seen in @sorcerers, […].", addl-bindings: (neg: neg, sg: sg, pl: pl))
Labeling uses the Typst #link("https://typst.app/docs/reference/model/figure/")[figure] document element. The `label-supplement`
parameter fills in the `suppliment` parameter of a `figure`, which is `[example]` by default.
== Styling lines of a gloss
== Styling <styling>
Each of the aforementioned text parameters has a corresponding style parameter,
formed by adding `-style` to its name: `header-style`, `source-style`,
@ -213,7 +95,8 @@ to or within any given content block in the gloss. Formatting applied in this
way will override any contradictory line-level formatting.
#codeblock(
"#gloss(
"#example(
(
header: [This text is about eating your head.],
header-style: text.with(weight: \"bold\", fill: green),
source: (text(fill:black)[I'm], [eat-ing], [your], [head]),
@ -221,9 +104,211 @@ way will override any contradictory line-level formatting.
morphemes: ([1#sg.#sbj\=to.be], text(fill:black)[eat-#prog], [2#sg.#poss], [head]),
morphemes-style: text.with(fill: blue),
translation: text(weight: \"bold\")[I'm eating your head!],
)
)", addl-bindings: (prog: prog, sbj: sbj, poss: poss, sg: sg))
//TODO add `line_styles` param
//TODO add `line-styles` param
An example for English which exhibits some additional styling, and uses imports from another file
for common glossing abbreviations:
#codeblock(
"#example(
(
source: ([I'm], [eat-ing], [your], [head]),
source-style: (item) => text(fill: red)[#item],
morphemes: ([1#sg.#sbj\=to.be], [eat-#prog], [2#sg.#poss], [head]),
morphemes-style: text.with(size: 10pt, fill: blue),
translation: text(weight: \"semibold\")[I'm eating your head!],
translation-style: (item) => [\"#item\"],
)
)
", addl-bindings: (poss: poss, prog: prog, sg: sg, sbj: sbj))
The `gloss()` function has three pre-defined parameters for glossing levels:
`source`, `transliteration`, and `morphemes`. It also has two parameters
for unaligned text: `header` for text that precedes the gloss, and
`translation` for text that follows the gloss.
The `morphemes` param can be skipped, if you just want to provide a source
text and translation, without a gloss:
#codeblock(
"#example(
(
source: ([Trato de entender, debo comprender, qué es lo que ha hecho conmigo],),
source-style: emph,
translation: [I try to understand, I must comprehend, what she has done with me],
)
)
")
Note that it is still necessary to wrap the `source` argument in an array of length one.
Here is an example of a lengthy gloss that forces a line break:
// adapted from https://brill.com/fileasset/downloads_static/static_publishingbooks_formatting_glosses_linguistic_examples.pdf
#codeblock(
"#example(
(
source: ([Ich],[arbeite],[ein],[Jahr],[um],[das],[Geld], [zu],[verdienen,],[das], [dein],[Bruder], [an],[einem],[Wochenende],[ausgibt.]),
source-style: text.with(weight: \"bold\"),
morphemes: ([I], [work],[ one], [year],[to],[the],[money],[to],[earn,], [that],[your],[brother],[on],[one], [weekend], [spends.]),
translation: [\"I work one year to earn the money that your brother spends in one weekend\"]
)
)", addl-bindings: (poss: poss, prog: prog, sg: sg, sbj: sbj))
== Additional lines <additional-lines>
To add more than three glossing lines, there is an additional parameter
`additional-lines` that can take a list of arbitrarily many more glossing
lines, which will appear below those specified in the aforementioned
parameters:
#codeblock(
"#example(
(
header: [Hunzib (van den Berg 1995:46)],
source: ([ождиг],[хо#super[н]хе],[мукъер]),
transliteration: ([oʒdig],[χõχe],[muqʼer]),
morphemes: ([ož-di-g],[xõxe],[m-uq'e-r]),
additional-lines: (
([boy-#smallcaps[obl]-#smallcaps[ad]], [tree(#smallcaps[g4])], [#smallcaps[g4]-bend-#smallcaps[pret]]),
([at boy], [tree], [bent]),
),
translation: [\"Because of the boy, the tree bent.\"]
)
)
")
== Sub-examples
Sub-examples can be achieved by adding more dictionaries of glossing fields, separated by commas.
A global `header` field for the set can be added.
#codeblock(
"#example(
header: [Coptic; transliterated and glossed based on _Coptic in 20 lessons_ (2007) by Layton Bently (§~28)],
(
header: [Indefinite articles],
source: ([hen-maein], [mn-hen-špêre]),
morphemes: ([#indf.#pl\-sign], [with-#indf.#pl\-wonder]),
translation: [signs and wonders]
),
(
header: [Definite articles],
source: ([m-maein], [mn-ne-špêre]),
morphemes: ([#def.#pl\-sign], [with-#def.#pl\-wonder]),
translation: [the signs and the wonders]
),
(
header: [Definite pronouns],
source: ([nei-maein], [mn-nei-špêre]),
morphemes: ([#dem.#pl\-sign], [with-#dem.#pl\-wonder]),
translation: [these signs and these wonders]
),
)
", addl-bindings: (indf: indf, pl: pl, sg: sg, def: def, dem: dem))
//TODO add a custom numbering system that can handle example 18a-c of Kartvelian Morphosyntax and Number Agreement
== Numbering Glosses
The `example()` function takes a boolean parameter `numbering` which will add an incrementing
count to each gloss. A function `numbered-example` is exported for convenience; this is
defined as simply `#let numbered-example = example.with(numbering: true)`, and is called with the
same arguments as `example()`:
#codeblock(
"#example(
(
source: ([გვ-ფრცქვნ-ი],),
transliteration: ([gv-prtskvn-i],),
morphemes: ([1#pl.#obj\-peel-#fmnt],),
translation: \"You peeled us\",
),
numbering: true,
)
#numbered-example(
(
source: ([მ-ფრცქვნ-ი],),
transliteration: ([m-prtskvn-i],),
morphemes: ([1#sg.#obj\-peel-#fmnt],),
translation: \"You peeled me\",
)
)
", addl-bindings: (pl: pl, obj: obj, sg: sg, fmnt: fmnt))
The displayed number for numbered glosses is iterated for each numbered gloss
that appears throughout the document. Unnumbered glosses do not increment the
counter for the numbered glosses.
The gloss count is controlled by the Typst counter variable `example-count`. This
variable can be imported from the `leipzig-gloss` package and manipulated using the
standard Typst counter functions to control gloss numbering:
#codeblock(
"#example-count.update(20)
#numbered-example(
(
header: [from _Standard Basque: A Progressive Grammar_ by Rudolf de Rijk, quoting P. Charriton],
source: ([Bada beti guregan zorion handi baten nahia.],),
translation: [There always is in us a will for a great happiness.],
)
)", addl-bindings: (example-count: example-count))
References to individual examples can be achieved using the `label` argument and the referencing mechanism of Typst:
#codeblock(
"See @sorcerers:
#numbered-example(
(
header: [Middle Welsh; modified from _Grammatical number in Welsh_ (1999) by Silva Nurmio (§~2.1.1)],
source: ([ac], [ny], [allvs], [y], [dewinyon], [atteb], [idav]),
morphemes: ([and], [#neg], [be_able.#smallcaps[pret].3#sg], [#smallcaps[def]], [sorcerer.#pl], [answer.#smallcaps[inf]], [to.3#sg.#smallcaps[m]]),
translation: [and the sorcerers could not answer him],
),
label: \"sorcerers\",
label-supplement: [Example]
)
As we have seen in @sorcerers, […].", addl-bindings: (neg: neg, sg: sg, pl: pl))
Labelling uses the Typst #link("https://typst.app/docs/reference/model/figure/")[figure] document element. The `label-supplement`
parameter fills in the `suppliment` parameter of a `figure`, which is `[example]` by default.
Note that `label` and `label-supplement` are top-level arguments of `example()` and `numbered-example()`, not of the interlinear glosses surrounded by `(` and `)`.
Labelling of sub-examples is possible as well, using the same `label` and `label-supplement` fields but within the parentheses surrounding the sub-example in question.
#codeblock(
"#numbered-example(
header: [Hausa; from _Toward a functional typology of adpositions_ (2022) by Zygmunt Frajzyngier (§~3.2)],
label: \"hausa\",
(
source: ([àkwai], [mutā̀nè], [dà], [yawā̀], [a], [kanṑ]),
morphemes: ([exist], [People], [#smallcaps[assc]], [many], [#pred], [Kano]),
translation: [There are a lot of people in Kano.],
label: \"people\"
),
(
source: ([àkwai], [makar̃antā], [a], [nan], [gàrin]),
morphemes: ([exist], [school], [#pred], [#dem], [town]),
translation: [There is a school in this town.],
label: \"school\",
),
)
In @hausa there are two sub-examples: @people deals with people and @school with a school.
", addl-bindings: (pred: pred, dem: dem))
= Standard Abbreviations
@ -242,11 +327,13 @@ accessed either with Typst field access notation or by importing them from
#codeblock(
"#import abbreviations: obl, sg, prf
#gloss(
#example(
(
header: [(from _Why Caucasian Languages?_, by Bernard Comrie, in _Endangered Languages of the Caucasus and Beyond_)],
source: ([\[qálɐ-m], [∅-kw-á\]], [ɬ’ə́-r]),
morphemes: ([city-#obl], [3#sg\-go-#prf], [man-#abbreviations.abs]),
translation: \"The man who went to the city.\"
)
)", addl-bindings: (abbreviations: abbreviations), unevaled-first-line: "#import \"leipzig-gloss.typ\": abbreviations")
@ -270,11 +357,13 @@ Custom abbreviations may be defined using the `abbreviations.emit-abbreviation`
#let ts = emit-abbreviation(\"TS\")
#gloss(
#example(
(
header: [(from _Georgian: A Structural Reference Grammar_, by George Hewitt)],
source: ([g-nax-av-en],),
morphemes: ([you#sub[2]-see(#fut)#sub[4]-#ts#sub[7]-they#sub[11]],),
translation: \"they will see you\",
)
)", addl-bindings: (abbreviations: abbreviations), unevaled-first-line: "#import \"leipzig-gloss.typ\": abbreviations")
== Building used-abbreviations pages
@ -290,103 +379,127 @@ These are the first twelve example glosses given in #link("https://www.eva.mpg.d
along with the Typst markup needed to generate them:
#{
gloss-count.update(0)
example-count.update(0)
}
#codeblock(
"#numbered-gloss(
"#numbered-example(
(
header: [Indonesian (Sneddon 1996:237)],
source: ([Mereka], [di], [Jakarta], [sekarang.]),
morphemes: ([they], [in], [Jakarta], [now]),
translation: \"They are in Jakarta now\",
)
)")
#codeblock(
"#numbered-gloss(
"#numbered-example(
(
header: [Lezgian (Haspelmath 1993:207)],
source: ([Gila], [abur-u-n], [ferma], [hamišaluǧ], [güǧüna], [amuq-da-č.]),
morphemes: ([now], [they-#obl\-#gen], [farm], [forever], [behind], [stay-#fut\-#neg]),
translation: \"Now their farm will not stay behind forever.\",
)
)", addl-bindings: (fut: fut, neg: neg, obl: obl, gen:gen))
#codeblock(
"#numbered-gloss(
"#numbered-example(
(
header: [West Greenlandic (Fortescue 1984:127)],
source: ([palasi=lu], [niuirtur=lu]),
morphemes: ([priest=and], [shopkeeper=and]),
translation: \"both the priest and the shopkeeper\",
)
)")
#codeblock(
"#numbered-gloss(
"#numbered-example(
(
header: [Hakha Lai],
source: ([a-nii -láay],),
morphemes: ([3#sg\-laugh-#fut],),
translation: [s/he will laugh],
)
)", addl-bindings: (sg: sg, fut: fut))
#codeblock(
"#numbered-gloss(
"#numbered-example(
(
header: [Russian],
source: ([My], [s], [Marko], [poexa-l-i], [avtobus-om], [v], [Peredelkino]),
morphemes: ([1#pl], [#com], [Marko], [go-#pst\-#pl], [bus-#ins], [#all], [Peredelkino]),
additional-lines: (([we], [with], [Marko], [go-#pst\-#pl], [bus-by], [to], [Peredelkino]),),
translation: \"Marko and I went to Perdelkino by bus\",
)
)", addl-bindings: (com: com, pl: pl, ins: ins, all: all, pst:pst))
#codeblock(
"#numbered-gloss(
"#numbered-example(
(
header: [Turkish],
source: ([çık-mak],),
morphemes: ([come.out-#inf],),
translation: \"to come out\",
)
)", addl-bindings: (inf: inf))
#codeblock(
"#numbered-gloss(
"#numbered-example(
(
header: [Latin],
source: ([insul-arum],),
morphemes: ([island-#gen\-#pl],),
translation: \"of the islands\",
)
)", addl-bindings: (gen:gen, pl: pl))
#codeblock(
"#numbered-gloss(
"#numbered-example(
(
header: [French],
source: ([aux], [chevaux]),
morphemes: ([to-#art\-#pl],[horse.#pl]),
translation: \"to the horses\",
)
)",addl-bindings: (art:art, pl:pl))
#codeblock(
"#numbered-gloss(
"#numbered-example(
(
header: [German],
source: ([unser-n], [Väter-n]),
morphemes: ([our-#dat\-#pl],[father.#pl\-#dat.#pl]),
translation: \"to our fathers\",
)
)", addl-bindings: (dat:dat, pl:pl))
#codeblock(
"#numbered-gloss(
"#numbered-example(
(
header: [Hittite (Lehmann 1982:211)],
source: ([n=an], [apedani], [mehuni],[essandu.]),
morphemes: ([#smallcaps[conn]=him], [that.#dat.#sg], [time.#dat.#sg], [eat.they.shall]),
translation: \"They shall celebrate him on that date\",
)
)", addl-bindings: (pl:pl, sg:sg, dat:dat))
#codeblock(
"#numbered-gloss(
"#numbered-example(
(
header: [Jaminjung (Schultze-Berndt 2000:92)],
source: ([nanggayan], [guny-bi-yarluga?]),
morphemes: ([who], [2#du.#A.3#sg.#P\-#fut\-poke]),
translation: \"Who do you two want to spear?\",
)
)", addl-bindings: (du:du, sg:sg, fut:fut, A:A, P:P))
#codeblock("
#numbered-gloss(
#numbered-example(
(
header: [Turkish (cf. 6)],
source: ([çık-mak],),
morphemes: ([come_out-#inf],),
translation: ['to come out'],
)
)", addl-bindings: (inf: inf))

View File

@ -1,6 +1,8 @@
#import "abbreviations.typ"
#let gloss-count = counter("gloss_count")
// ╭─────────────────────╮
// │ Interlinear glosses │
// ╰─────────────────────╯
#let build-gloss(item-spacing, formatters, gloss-line-lists) = {
assert(gloss-line-lists.len() > 0, message: "Gloss line lists cannot be empty")
@ -13,23 +15,23 @@
assert(formatters.len() == gloss-line-lists.len(), message: "The number of formatters and the number of gloss line lists should be equal")
let make_item_box(..args) = {
let make-item-box(..args) = {
box(stack(dir: ttb, spacing: 0.5em, ..args))
}
for item_index in range(0, len) {
for item-index in range(0, len) {
let args = ()
for (line_idx, formatter) in formatters.enumerate() {
let formatter_fn = if formatter == none {
for (line-idx, formatter) in formatters.enumerate() {
let formatter-fn = if formatter == none {
(x) => x
} else {
formatter
}
let item = gloss-line-lists.at(line_idx).at(item_index)
args.push(formatter_fn(item))
let item = gloss-line-lists.at(line-idx).at(item-index)
args.push(formatter-fn(item))
}
make_item_box(..args)
make-item-box(..args)
h(item-spacing)
}
}
@ -37,90 +39,129 @@
// a workround so we can use `label` as a variable name where it is shadowed by the function param `label`
// Once typst version 0.12 with https://github.com/typst/typst/pull/4038 is released we should be able
// to replace this workaround with `std.label`
#let cmdlabel = label
#let cmdlabel = label
// 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()`.
#let gloss(
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,
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,
) = {
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()
}
let formatters = (source-style,)
let gloss-line-lists = (source,)
if transliteration != none {
formatters.push(transliteration-style)
gloss-line-lists.push(transliteration)
}
if morphemes != none {
formatters.push(morphemes-style)
gloss-line-lists.push(morphemes)
}
for additional in additional-lines {
formatters.push(none) //TODO fix this
gloss-line-lists.push(additional)
}
build-gloss(item-spacing, formatters, gloss-line-lists)
if translation != none {
linebreak()
if translation-style == none {
translation
} else {
translation-style(translation)
}
}
}
align(left)[#gloss-items]
}
// ╭─────────────────────╮
// │ Linguistic examples │
// ╰─────────────────────╯
#let example-count = counter("example-count")
#let example(
label: none,
label-supplement: [example],
item-spacing: 1em,
gloss-padding: 2.0em, //TODO document these
gloss-padding: 2.5em, //TODO document these
left-padding: 0.5em,
numbering: false,
breakable: false,
..args
) = {
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()
let add-subexample(subexample, count) = {
// Remove parameters which are not used in the `gloss`.
// TODO Make this functional, if (or when) its 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")
}
let formatters = (source-style,)
let gloss-line-lists = (source,)
if transliteration != none {
formatters.push(transliteration-style)
gloss-line-lists.push(transliteration)
}
if morphemes != none {
formatters.push(morphemes-style)
gloss-line-lists.push(morphemes)
}
for additional in additional-lines {
formatters.push(none) //TODO fix this
gloss-line-lists.push(additional)
}
build-gloss(item-spacing, formatters, gloss-line-lists)
if translation != none {
linebreak()
if translation-style == none {
translation
} else {
translation-style(translation)
}
if "label-supplement" in subexample-internal {
let _ = subexample-internal.remove("label-supplement")
}
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)
)
) #if "label" in subexample {cmdlabel(subexample.label)}
]
]
}
if numbering {
gloss-count.step()
example-count.step()
}
let gloss_number = if numbering {
[(#context gloss-count.display())]
let example-number = if numbering {
[(#context example-count.display())]
} else {
none
}
@ -128,15 +169,32 @@
style(styles => {
block(breakable: breakable)[
#figure(
kind: "ling-example",
kind: "example",
numbering: it => [#example-count.display()],
outlined: false,
supplement: label-supplement,
numbering: it => [#gloss-count.display()],
stack(
dir: ltr, //TODO this needs to be more flexible
left-padding,
[#gloss_number],
gloss-padding - left-padding - measure([#gloss_number],styles).width,
align(left)[#gloss_items],
[#example-number],
gloss-padding - left-padding - measure([#example-number],styles).width,
{
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)
}
}
}
),
) #if label != none {cmdlabel(label)}
]
@ -144,4 +202,4 @@
)
}
#let numbered-gloss = gloss.with(numbering: true)
#let numbered-example = example.with(numbering: true)