diff --git a/documentation.pdf b/documentation.pdf index 99b3722..cd1a609 100644 Binary files a/documentation.pdf and b/documentation.pdf differ diff --git a/documentation.typ b/documentation.typ index f01158a..7080d88 100644 --- a/documentation.typ +++ b/documentation.typ @@ -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 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 + +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)) diff --git a/leipzig-gloss.typ b/leipzig-gloss.typ index b5affa8..3bdc15e 100644 --- a/leipzig-gloss.typ +++ b/leipzig-gloss.typ @@ -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) 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") } - - 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)