diff --git a/glossy/README.md b/glossy/README.md index bc23cc3..18c033c 100644 --- a/glossy/README.md +++ b/glossy/README.md @@ -128,7 +128,8 @@ And then loaded during initialization as follows: `init-glossary()` supports the following parameters: -- `format-term` // TODO: document this +- `format-term` // Function deciding how to format a term, depending on + the 'mode' (short, long, both). See example below. - `show-term` // Function which can customize display of a term (see example below) - `term-links: false` // True if you want terms to link to their glossary entry @@ -141,19 +142,27 @@ In modern web development, languages like @html and @css are essential. The @tps:pl need to be submitted by Friday. ``` -Available modifiers: +Available *modes* (they determine which information is printed and are +mutually exclusive): -- **cap**: Capitalizes the term -- **pl**: Uses the plural form +- auto: (Default) Shows the form depending on the previous usage. + The mode when no other mode is appended with a colon. - **both**: Shows "Long Form (Short Form)" - **short**: Shows only short form - **long**: Shows only long form - **def** or **desc**: Shows the description +- See [the reference](#reference-for-using-glossary-terms) + for a complete overview. + +Available *modifiers* (they modify how this information is printed): + +- **cap**: Capitalizes the term +- **pl**: Uses the plural form - **noref** or **noindex**: Don't show the term in the glossary. - **a** or **an**: Include the article (`an` is just an alias of `a`, they're equivalent) -Modifiers can be combined with colons: +Modes and modifiers can be combined with colons: | **Input** | **Output** | | ------------------- | -------------------------------------------------------------- | @@ -198,21 +207,71 @@ which would normally render as something like "test procedure specification." But for some reason you want it to actually say "an annoying report." You can do that like this: -| **Input** | **Output** | -| ------------------------- | -------------------- | -| `@tps[an annoying report` | "an annoying report" | +| **Input** | **Output** | +| -------------------------- | -------------------- | +| `@tps[an annoying report]` | "an annoying report" | +| `@tps[]` | #none | + +In case you want to reference a term from the Glossary/Index, but don't +want to display it for whatever reason, the second form can be used. ### First use logic -Any use of the `short`, `long`, or `both` modifier will not count against the -term's first use. +The 'mode' of a glossary term determines which information is printed about this +term. When using a plain `@term`, possibly with some modifiers, the `auto` mode +is selected. This mode displays the form depending on the usage counter. On the +first use (`usage counter == 0`), 'both' forms are printed. On subsequent usage +(`usage counter > 0`) the 'short' form is printed. -This is useful, for example, when you want to use a term in a heading or a -caption. In that case, your term might end up in an `outline()` at the top of +Not all occurrences of a term are counted towards its usage, this depends on +the mode of the term. Furthermore, this can be manually controlled by modifiers. +Both are documented in the [reference tables](#reference-for-using-glossary-terms). + +This control over the usage counter is practical, for example, when you want to +use a term in a heading or a caption. +In that case, your term might end up in an `outline()` at the top of your document. Normally you don't want that to count as a term's first real use. Normally you want that to happen in the body of your document. So, by using -these modifiers in such situations, you can not only specify exactly how you -want your term to appear, but also control whether it counts as a "first use". +the appropriate mode and modifiers in such situations, you can not only specify +exactly how you want your term to appear, but also control whether it counts +as a "first use". + +### Reference for Using Glossary Terms + +A reference about +- the different 'modes' and how they determine which information is printed about a term +- the impact of modes on the [usage counter and ensuing first use behaviour](#first-use-logic) +- how modifiers can change the behaviour with respect to the usage counter +- how modifiers impact the presentation of the information printed + +A glossary term can display different types of information, which is determined +by the 'mode' in which it is printed. These modes are all mutually exclusive. + +| Mode | Utilization | Default first use behaviour | Description | +| ------------- | ------------------- | --------------------------- | ----------- | +| `auto` | `@term` | use | The default mode when no 'mode' modifier or supplement is specified. The form depends on the first use counter. | +| `both` | `:both` | use | Shows both forms of the term, by default ([see `format-term`](#customizing-term-display)) like "Long Form (Short Form)". Falls back to short when long form not available. | +| `short` | `:short` | no-use | Shows only short form. | +| `long` | `:long` | no-use | Shows only long form. Falls back to short when long form not available. | +| `description` | `:def`
`:desc` | no-use | Shows the description (None of the modifiers apply here & no link is created towards the glossary). | +| `supplement` | `[content]` | no-use | Shows the content given by [the supplement](#overriding-term-text). | +| `reset` | `:reset` | reset usage counter to 0 | Don't output any content & don't link to the glossary. Typical usage would be after an abstract or even before the start of each chapter, maybe in an injected rule. (This could be made a modifier like `:use` or `:nouse`, but would mostly be utilized as `@term:reset:noindex[]` anyways, thus this is a mode. Write `@term@term:reset` to utilize it as a modifier.) | + +Minor modifications to how these different types of information are printed, +linked or counted towards the usage counter, are controlled by the 'modifiers'. +They do not influence the default first use behaviour of the mode (unless +explicitely created for this purpose). Moreover, they neatly compose together +and with most modes (some exceptions exists, but will print a clear error - +report a bug if not). + +| Modifier | Description | +| ------------------------------ | ---------------------------------------------------------------------- | +| `:cap` | Capitalizes the term | +| `:pl` | Uses the plural form | +| `:noref`
`:noindex` | Don't show the term in the glossary (i.e. no page will be linked - a link from the term towards the glossary is always provided.). | +| `:a`
`a:`
`:an`
`an:` | Include the article (`an` is just an alias of `a`, they're equivalent) | +| `:use`
`:spend` | After this occurrence, the term is 'used', i.e. the usage counter is increased by 1. Write `@term:use:noindex[]` to uniquely control the usage counter without output. | +| `:nouse`
`:nospend` | After this occurrence, the usage counter remains exactly as the same as before. As if the term is transparent with respect to the usage counter. | ### Generating the Glossary @@ -244,7 +303,7 @@ Or to just show the empty group (i.e. terms without a group): ### Customizing Term Display -Control how terms appear in the document by providing a custom `show-term` function: +Control how terms are styled in the document by providing a custom `show-term` function: ```typst #let emph-term(term-body) = { emph(term-body) } @@ -255,6 +314,26 @@ Control how terms appear in the document by providing a custom `show-term` funct ) ``` +Terms can be formatted depending on the 'mode' by providing a custom +`format-term` function. The mode is one of ("short", "long", "both"). + +```typst +// When displaying both, reverse the display order by showing +// "short (long)" instead of the normal "long (short)" +#let short-long-term(mode, short-form, long-form) = { + if mode == "short" { short-form } + else if mode == "long" { long-form } + else { // mode assumed to be "both" + short-form + " (" + long-form + ")" + } +} + +#show: init-glossary.with( + myGlossary, + format-term: short-long-term +) +``` + ### Glossary Themes #### Included Themes @@ -300,7 +379,7 @@ Customize glossary appearance by defining a theme with three functions: columns: (auto, 1fr, auto), output, repeat([#h(0.25em) . #h(0.25em)]), - entry.pages, + entry.pages.join(", "), ) ) } @@ -313,7 +392,7 @@ Entry fields available to themes: - `long`: Long form (can be `none`) - `description`: Term description (can be `none`) - `label`: Term's dictionary label -- `pages`: Linked page numbers where term appears +- `pages`: Array of linked page numbers where term appears **NOTE:** If the theme does not emit `entry.label`, linking from terms to their glossary entry will not work. @@ -321,3 +400,16 @@ glossary entry will not work. ## License This project is licensed under the MIT License. + +## Changelog + +### v0.9.0 +- **Breaking**: In themes, entry.pages now returns an array of linked + page numbers, instead of opaque content. Use `#entry.pages.join(", ")` + in your custom theme to keep the previous behaviour. +- **Breaking**: The `both` mode (previously called modifier) by default + now counts towards the first use of a term. +- **Breaking**: When multiple conflicting modes are supplied (like 'short' + and 'long'), glossy now throws an error (with a clear message). +- **Breaking**: When an unrecognized modifier is supplied with the term, + glossy will now panic instead of ignoring the modifier. diff --git a/glossy/src/gloss.typ b/glossy/src/gloss.typ index eed3f34..2429fd4 100644 --- a/glossy/src/gloss.typ +++ b/glossy/src/gloss.typ @@ -135,7 +135,8 @@ label(__gloss_label_prefix + key + __gloss_entry_postfix) } -// Updates the term usage in the glossary state. +// Updates the term usage (first use counter & wants to be referenced counter) +// in the glossary state. // // Parameters: // key (string): The glossary entry key @@ -143,14 +144,16 @@ // Returns: // none: Updates state as a side effect // -#let __mark_term_used(key, count-as-first-use) = { - counter(__gloss_label_prefix + key).step() +#let __mark_term_used(key, count-as-referenced, count-as-first-use) = { + if count-as-referenced { + counter(__gloss_label_prefix + key).step() + } if count-as-first-use { counter(__gloss_label_prefix + key + __gloss_first_use_counter_postfix).step() } } -// Queries whether the term is used ANYWHERE +// Queries whether the term wants to be referenced from ANYWHERE // // Parameters: // key (string): The glossary entry key @@ -159,7 +162,7 @@ // Returns: // boolean: If the entry is used above the location in the document // -#let __is_term_ever_used(key, location: none) = { +#let __is_term_ever_referenced(key, location: none) = { let c = counter(__gloss_label_prefix + key) c = if location == none {c.final()} else {c.at(location)} c.at(0) > 0 @@ -183,6 +186,18 @@ c.at(0) > 0 } +// Reset the first use counter to 0 +// +// Parameters: +// key (string): The glossary entry key +// +// Returns: +// none: Updates state as a side effect +// +#let __reset_term_first_used(key, location: none) = { + counter(__gloss_label_prefix + key + __gloss_first_use_counter_postfix).update(0) +} + // Determine if the glossary contains a visible entry // // Parameters: @@ -207,8 +222,6 @@ // - "long": Show only the long form. // - "def" or "desc": Show the term's description instead of its name. // - "a" or "an": Prepend an article, chosen from the entry (short or long form). -// - "hide" or "hidden": Use term (for the sake of the glossary/index), -// but don't display it. // show-term (function): A function that renders the chosen term. // term-links (boolean): If terms should be clickable links leading to glossary // @@ -223,18 +236,68 @@ // content: The fully formatted term, including optional article, capitalization, // usage tracking metadata, and the chosen form (short, long, or both). // -#let __gls(key, modifiers: array, format-term: function, show-term: function, term-links: true, display-text: none) = { +#let __gls(key, modes-modifiers: array, format-term: function, show-term: function, term-links: true, display-text: none) = { + let possible_modes = ("auto", "both", "short", "long", "supplement", "description", "reset") // --------------------------------------------------------------------------- - // Check for illegal modifier combinations + // Normalize mode & modifier inputs AND check if the modifiers are valid // --------------------------------------------------------------------------- - if ("def" in modifiers or "desc" in modifiers) and modifiers.len() > 1 { - panic("Cannot use 'def'/'desc' with other modifiers.") + let modes-modifiers = modes-modifiers.map(it => { + if it == "def" or it == "desc" { + return "description" + } else if it == "a" or it == "an" { + return "an" + } else if it == "use" or it == "spend" { + return "use" + } else if it == "nouse" or it == "nospend" { + return "no-use" + } else if it == "noref" or it == "noindex" { + return "noindex" + } else if it in ("both", "short", "long", "reset", "cap", "pl") { + return it + } else if it in ("auto", "description", "supplement") { + panic(it, "is a valid mode, but is not used by applying a modifier with this name. Read the documentation.") + } else { + panic(it, "is not a recognized mode or modifier.") + } + }) + if display-text != none and display-text != auto { + // A supplement is provided, thus the mode is "supplement" + modes-modifiers.push("supplement") } - if ("a" in modifiers or "an" in modifiers) and ("pl" in modifiers) { + + // --------------------------------------------------------------------------- + // Determine the requested modes (plural) and the modifier array + // --------------------------------------------------------------------------- + modes-modifiers.push("__MIDDLE_ITEM") // unused modifier + let (requested_modes, modifiers) = modes-modifiers.sorted(key: it => { + if it in possible_modes { + // It is a 'mode' + return -1 + } else if it == "__MIDDLE_ITEM" { + return 0 + } else { + // It is a 'modifier' + return 1 + } + }).split("__MIDDLE_ITEM") + + // --------------------------------------------------------------------------- + // Check for illegal mode and/or modifier combinations + // --------------------------------------------------------------------------- + if requested_modes.len() > 1 { + panic("Cannot mix modes ", requested_modes, ", pick one.") + } + if "description" in requested_modes and modifiers.len() > 0 { + panic("Cannot use mode 'def'/'desc' with other modifiers.") + } + if "reset" in requested_modes and modifiers.len() > 0 { + panic("Cannot use mode 'reset' with other modifiers.") + } + if "an" in modifiers and "pl" in modifiers { panic("Cannot use 'a'/'an' and 'pl' together.") } - if ("hidden" in modifiers or "hide" in modifiers) and modifiers.len() > 1{ - panic("Cannot use 'hide'/'hidden' with other modifiers.") + if "use" in modifiers and "no-use" in modifiers { + panic("Cannot use 'use'/'spend' and 'nouse'/'nospend' together.") } // --------------------------------------------------------------------------- @@ -243,24 +306,41 @@ let entry = __get_entry(key) // --------------------------------------------------------------------------- - // If "def" or "desc" modifier is present, show the entry's description immediately + // Determine the requested mode (singular) // --------------------------------------------------------------------------- - if "def" in modifiers or "desc" in modifiers { + let requested_mode = if requested_modes.len() == 0 { + "auto" + } else { + requested_modes.at(0) + } + + // --------------------------------------------------------------------------- + // If mode is "description", show the entry's description immediately + // --------------------------------------------------------------------------- + if requested_mode == "description" { if entry.description == none { panic("Use of 'def'/'desc' requires a description be defined.") } return show-term([#entry.description]) } + // --------------------------------------------------------------------------- + // If mode is "reset", reset the (first) usage counter and return immediately + // --------------------------------------------------------------------------- + if requested_mode == "reset" { + __reset_term_first_used(key) + return + } // --------------------------------------------------------------------------- // Manage term usage counting and determine if it's the first reference // --------------------------------------------------------------------------- let is_first_use = not __is_term_first_used(key, location: here()) - let count-as-first-use = ("short" not in modifiers and "long" not in modifiers and "both" not in modifiers) - let wants_reference = ("noref" not in modifiers and "noindex" not in modifiers) - if wants_reference { - __mark_term_used(key, count-as-first-use) - } + let default-count-as-first-use = (requested_mode == "auto" or requested_mode == "both") + let force-count-as-first-use = "use" in modifiers + let force-skip-as-first-use = "no-use" in modifiers + let wants_reference = "noindex" not in modifiers + __mark_term_used(key, wants_reference, force-count-as-first-use + or (not force-skip-as-first-use and default-count-as-first-use)) // --------------------------------------------------------------------------- // Helper Functions @@ -282,7 +362,7 @@ let capitalize_term = (article, term) => { if "cap" not in modifiers { (article, term) - } else if article == "" { + } else if article == none { // No article present, so capitalize the term's first letter. (article, upper(term.first()) + term.slice(1)) } else { @@ -292,14 +372,14 @@ } // get_article(mode): Returns the appropriate article string based on the mode. - // - If wants_article is false, returns an empty string. + // - If wants_article is false, returns none // - If mode is "short", return the short article. // - If mode is "long" or "both", return the long article. // The calling logic ensures that when mode = "long" or "both", a long form exists. let get_article = (mode) => { - let wants_article = "a" in modifiers or "an" in modifiers + let wants_article = "an" in modifiers if not wants_article { - "" + none } else if mode == "short" { entry.article + " " } else { @@ -308,67 +388,38 @@ } } - // determine_mode(wants_both, wants_long, wants_short, is_first_use, long_available): - // Decides which mode ("short", "long", or "both") to use. - // Preference is given to explicit modifiers. If the requested mode can't be fulfilled due to no long form, - // fall back to "short". - // If no explicit mode is given, the default behavior is: - // - On first use and if a long form exists, "both". - // - Otherwise, "short". - let determine_mode = (wants_both, wants_long, wants_short, is_first_use, long_available, wants_hidden) => { - if wants_hidden { - // If it's hidden, nothing else matters - "hidden" - } else if wants_both { - // Explicit request for "both": - // If a long form exists, use "both", otherwise fall back to "short". - if long_available { - "both" - } else { - "short" - } - } else if wants_long { - // Explicit request for "long": - // If a long form exists, use "long", otherwise fall back to "short". - if long_available { - "long" - } else { - "short" - } - } else if wants_short { - // Explicit request for "short": - // Always "short" regardless of availability. - "short" - } else { - // No explicit mode given: - // On first use with a long form available, default to "both". - // Otherwise, use "short". - if is_first_use and long_available { - "both" - } else { - "short" - } - } - } - // --------------------------------------------------------------------------- // Determine desired options based on modifiers and entry.long availability // --------------------------------------------------------------------------- - let wants_both = "both" in modifiers - let wants_long = "long" in modifiers - let wants_short = "short" in modifiers - let wants_hidden = ("hidden" in modifiers or "hide" in modifiers) - let long_available = entry.long != none + // Decide which mode ("short", "long", or "both" or "supplement") to use. + // If the requested mode can't be fulfilled due to no long form, fall back to "short". + // If no explicit mode is given (i.e. we are in 'auto' mode), the default behavior is: + // - On first use and if a long form exists, "both". + // - Otherwise, "short". + if requested_mode == "auto" { + // Set requested_mode to automatically determined mode + requested_mode = if is_first_use { + "both" + } else { + "short" + } + } - // Determine mode using the helper function - let mode = determine_mode(wants_both, wants_long, wants_short, is_first_use, long_available, wants_hidden) + let long_available = entry.long != none + let mode = if (requested_mode == "both" or requested_mode == "long") and not long_available { + // The fall back case, because long form is not available, but it is requested + "short" + } else { + // No fall back needed, the requested mode can be fulfilled + requested_mode + } // Pluralize (if requested) let short-form = pluralize_term(entry.short, entry.plural) let long-form = pluralize_term(entry.long, entry.longplural) // `none` safe here // Apply format-term() to get the term - let formatted-term = if display-text != none and display-text != auto { + let formatted-term = if mode == "supplement" { // Display text was overriden by user, just accept it (string or content) display-text } else { @@ -380,7 +431,7 @@ // that with content, thus requiring a string here. // TODO: consider if we want to capitalize *before* this. First intuition is // "no". - panic("Your cutsom format-term() function must return a string.") + panic("Your custom format-term() function must return a string.") } // Get the article, then capitalize either the article or term (if requested) @@ -390,7 +441,7 @@ // Construct and return the final output // --------------------------------------------------------------------------- context { - let linked-term = if term-links and query(__entry_label(key)).len() > 0 { + let linked-term = if term-links and __has_glossary_entry(key) { link(__entry_label(key), term) } else { term @@ -403,40 +454,33 @@ } // Create the output content - if mode == "hidden" { - // just put the metadata+label out there - [#metadata("hidden term")#term-label] - } else { - // normal term display (ie not hidden) - [#article#show-term(linked-term)#metadata(term)#term-label] - // |^^^^^|^^^^^^^^^ |^^^^^^^^ |^^^^^^^^^^^^ - // \_art.| | | - // \_ apply user formatting function to the term - // | | - // \_ metadata lets us label (ie makes it "labelable") - // | - // \_ i.e. <__gloss:key>, etc. - // for backlink from glossary - } + [#article#show-term(linked-term)#metadata(term)#term-label] + // |^^^^^|^^^^^^^^^ |^^^^^^^^ |^^^^^^^^^^ + // | | | \_ the label for backlink from + // | | | glossary i.e. <__gloss:key>, etc. + // | | \_ metadata lets us label (ie makes it "labelable") + // | \_ apply user formatting function to the term + // \_art. } } // Creates page number links back to each usage of a glossary term. // -// This function generates a comma-separated list of page numbers, where each -// number links back to a usage of the term in the document. Duplicate page -// numbers are removed to avoid redundancy. +// This function generates an array of page numbers, where each number links +// back to a usage of the term in the document. Duplicate page numers are +// removed to avoid redundancy. // // Parameters: // key (string): The glossary entry key // // Returns: -// content: Comma-separated list of linked page numbers +// array: Linked page numbers // -// Example output: "1, 3, 5" where each number links to the term usage on that page +// Example output after a typical .join(", "): +// "1, 3, 5" where each number links to the term usage on that page // #let __create_backlinks(key) = { - return context query(__term_label(key)) // find all reference + return query(__term_label(key)) // find all reference .map(meta => { // extract location and page number (or symbol) let loc = meta.location() let page = numbering(__default(loc.page-numbering(), "1"), ..counter(page).at(loc)) @@ -444,7 +488,6 @@ }) .dedup(key: ((loc, page)) => page) // deduplicate by page .map(((loc, page)) => link(loc, page)) // create links - .join(", ") } // Formats a term string based on the specified mode. @@ -559,7 +602,7 @@ // Now see if this is an actual glossary term key if __has_entry(key) { // Found in dictionary, render via __gls() - __gls(key, modifiers: modifiers.map(lower), format-term: format-term, show-term: show-term, term-links: term-links, display-text: supplement) + __gls(key, modes-modifiers: modifiers.map(lower), format-term: format-term, show-term: show-term, term-links: term-links, display-text: supplement) } else { // Not one of ours, so just pass it through r @@ -592,7 +635,7 @@ // - long: Long form of term (optional) // - description: Term description (optional) // - label: Term's dictionary label -// - pages: Linked page numbers where term appears +// - pages: Array of linked page numbers where term appears // - index: Zero-based entry index within group // - total: Total entries in group // groups (array): Optional list of groups to include @@ -622,7 +665,7 @@ let output = (:) let all_entries = __gloss_entries.final() let all_used = if not show-all { - all_entries.keys().filter(key => __is_term_ever_used(key)) + all_entries.keys().filter(key => __is_term_ever_referenced(key)) } else { all_entries.keys() } diff --git a/glossy/src/themes/academic.typ b/glossy/src/themes/academic.typ index 975c8ad..8da8513 100644 --- a/glossy/src/themes/academic.typ +++ b/glossy/src/themes/academic.typ @@ -40,7 +40,7 @@ columns: (1fr, auto), gutter: 0.75em, [#short-display#long-display#description#entry.label], - text(fill: rgb("#666666"), entry.pages) + text(fill: rgb("#666666"), entry.pages.join(", ")) ) } ) diff --git a/glossy/src/themes/basic.typ b/glossy/src/themes/basic.typ index b756c27..a36f04d 100644 --- a/glossy/src/themes/basic.typ +++ b/glossy/src/themes/basic.typ @@ -36,7 +36,7 @@ // - long: Long form of term (optional) // - description: Term description (optional) // - label: Term's dictionary label - // - pages: Linked page numbers where term appears + // - pages: Array of linked page numbers where term appears // index: Zero-based entry index within group // total: Total entries in group entry: (entry, index, total) => { @@ -62,7 +62,7 @@ left: 1em, bottom: 0.5em, block( - [#term#entry.label#long-form#description #h(1em) (pp. #entry.pages)] + [#term#entry.label#long-form#description #h(1em) (pp. #entry.pages.join(", "))] ) ) ) diff --git a/glossy/src/themes/chicago-index.typ b/glossy/src/themes/chicago-index.typ index 006c440..bdfb526 100644 --- a/glossy/src/themes/chicago-index.typ +++ b/glossy/src/themes/chicago-index.typ @@ -38,7 +38,7 @@ columns: (1fr, auto), gutter: 1em, [#short-display#entry.label#long-display#description#entry.label], - [#entry.pages] + [#entry.pages.join(", ")] ) } ) diff --git a/glossy/src/themes/compact.typ b/glossy/src/themes/compact.typ index 86a40e3..238fe94 100644 --- a/glossy/src/themes/compact.typ +++ b/glossy/src/themes/compact.typ @@ -44,7 +44,7 @@ // - long: Long form of term (optional) // - description: Term description (optional) // - label: Term's dictionary label - // - pages: Linked page numbers where term appears + // - pages: Array of linked page numbers where term appears // index: Zero-based entry index within group // total: Total entries in group entry: (entry, index, total) => { @@ -83,7 +83,7 @@ text( size: 0.6em, fill: gray.darken(20%), - entry.pages + entry.pages.join(", ") ) ) ) diff --git a/glossy/src/themes/table.typ b/glossy/src/themes/table.typ index 1dfe1f9..4a9400b 100644 --- a/glossy/src/themes/table.typ +++ b/glossy/src/themes/table.typ @@ -27,6 +27,6 @@ }, entry: (entry, index, total) => { - (entry.short + entry.label, entry.long, entry.description, entry.pages) + (entry.short + entry.label, entry.long, entry.description, entry.pages.join(", ")) } ) diff --git a/glossy/src/themes/twocol.typ b/glossy/src/themes/twocol.typ index 7183e6e..068446b 100644 --- a/glossy/src/themes/twocol.typ +++ b/glossy/src/themes/twocol.typ @@ -36,7 +36,7 @@ // - long: Long form of term (optional) // - description: Term description (optional) // - label: Term's dictionary label - // - pages: Linked page numbers where term appears + // - pages: Array of linked page numbers where term appears // index: Zero-based entry index within group // total: Total entries in group entry: (entry, index, total) => { @@ -65,7 +65,7 @@ [#short-display#entry.label#long-display#description], // Term with label [#repeat(h(0.25em) + "." + h(0.25em))], // Dotted leader line [ . ], // A 1em wide dot so we definitely get some break between term and pages - [#entry.pages] // Page references + [#entry.pages.join(", ")] // Page references ) ) }, diff --git a/glossy/tests/abbreviated-syntax/ref/1.png b/glossy/tests/abbreviated-syntax/ref/1.png index 7b6a7b4..d1279bd 100644 Binary files a/glossy/tests/abbreviated-syntax/ref/1.png and b/glossy/tests/abbreviated-syntax/ref/1.png differ diff --git a/glossy/tests/abbreviated-syntax/test.typ b/glossy/tests/abbreviated-syntax/test.typ index 369ec55..cc1a805 100644 --- a/glossy/tests/abbreviated-syntax/test.typ +++ b/glossy/tests/abbreviated-syntax/test.typ @@ -40,18 +40,29 @@ Now make sure I get your @tps:short reports by 2pm! == Using conflicting modifiers (short, long, both) -These modifiers are semantically mutually exclusive, by they can be combined -syntactically. When multiple are used, `glossy` tries to make a smart choice on -which one to display. +These modifiers are semantically mutually exclusive. When multiple are used, +`glossy` throws an error to the compiler, explaining the problem. + +#import "/src/gloss.typ": __gls, __default-format-term +#let manual-gls(key, mods) = __gls(key, modes-modifiers: mods, format-term: __default-format-term, show-term: (body) => [#strong(body)], term-links: false, display-text: none) +#assert-panic(manual-gls.with("tps", ("short", "long"))) +#let msg-short-long = "panicked with: \"Cannot mix modes \", (\"short\", \"long\"), \", pick one.\"" +#let msg-short-both = "panicked with: \"Cannot mix modes \", (\"short\", \"both\"), \", pick one.\"" +#let msg-long-both = "panicked with: \"Cannot mix modes \", (\"long\", \"both\"), \", pick one.\"" +#let msg-short-long-both = "panicked with: \"Cannot mix modes \", (\"short\", \"long\", \"both\"), \", pick one.\"" #table( columns: 2, - table.header([*Input*], [*Output*]), + table.header([*Input*], [*Error*]), - [`@tps:short:long` ], [@tps:short:long], - [`@tps:short:both` ], [@tps:short:both], - [`@tps:long:both` ], [@tps:long:both], - [`@tps:short:long:both`], [@tps:short:long:both], + [`@tps:short:long` ], [#assert.eq(catch(manual-gls.with("tps", ("short", "long"))), + msg-short-long)#msg-short-long], + [`@tps:short:both` ], [#assert.eq(catch(manual-gls.with("tps", ("short", "both"))), + msg-short-both)#msg-short-both], + [`@tps:long:both` ], [#assert.eq(catch(manual-gls.with("tps", ("long", "both"))), + msg-long-both)#msg-long-both], + [`@tps:short:long:both`], [#assert.eq(catch(manual-gls.with("tps", ("short", "long", "both"))), + msg-short-long-both)#msg-short-long-both], ) #let my-theme = ( @@ -79,7 +90,7 @@ which one to display. columns: (auto, 1fr, auto), output, repeat([#h(0.25em) . #h(0.25em)]), - entry.pages, + entry.pages.join(", "), ) ) } diff --git a/glossy/tests/demo/ref/1.png b/glossy/tests/demo/ref/1.png index f5208fd..dfa7f93 100644 Binary files a/glossy/tests/demo/ref/1.png and b/glossy/tests/demo/ref/1.png differ diff --git a/glossy/tests/demo/test.typ b/glossy/tests/demo/test.typ index 0a422bb..71e1eaf 100644 --- a/glossy/tests/demo/test.typ +++ b/glossy/tests/demo/test.typ @@ -78,18 +78,29 @@ Articles are incompatible with the `pl` (plural) modifier. == Using conflicting modifiers (short, long, both) -These modifiers are semantically mutually exclusive, by they can be combined -syntactically. When multiple are used, `glossy` tries to make a smart choice on -which one to display. +These modifiers are semantically mutually exclusive. When multiple are used, +`glossy` throws an error to the compiler, explaining the problem. + +#import "/src/gloss.typ": __gls, __default-format-term +#let manual-gls(key, mods) = __gls(key, modes-modifiers: mods, format-term: __default-format-term, show-term: (body) => [#strong(body)], term-links: false, display-text: none) +#assert-panic(manual-gls.with("tps", ("short", "long"))) +#let msg-short-long = "panicked with: \"Cannot mix modes \", (\"short\", \"long\"), \", pick one.\"" +#let msg-short-both = "panicked with: \"Cannot mix modes \", (\"short\", \"both\"), \", pick one.\"" +#let msg-long-both = "panicked with: \"Cannot mix modes \", (\"long\", \"both\"), \", pick one.\"" +#let msg-short-long-both = "panicked with: \"Cannot mix modes \", (\"short\", \"long\", \"both\"), \", pick one.\"" #table( columns: 2, - table.header([*Input*], [*Output*]), + table.header([*Input*], [*Error*]), - [`@tps:short:long` ], [@tps:short:long], - [`@tps:short:both` ], [@tps:short:both], - [`@tps:long:both` ], [@tps:long:both], - [`@tps:short:long:both`], [@tps:short:long:both], + [`@tps:short:long` ], [#assert.eq(catch(manual-gls.with("tps", ("short", "long"))), + msg-short-long)#msg-short-long], + [`@tps:short:both` ], [#assert.eq(catch(manual-gls.with("tps", ("short", "both"))), + msg-short-both)#msg-short-both], + [`@tps:long:both` ], [#assert.eq(catch(manual-gls.with("tps", ("long", "both"))), + msg-long-both)#msg-long-both], + [`@tps:short:long:both`], [#assert.eq(catch(manual-gls.with("tps", ("short", "long", "both"))), + msg-short-long-both)#msg-short-long-both], ) #let my-theme = ( @@ -117,7 +128,7 @@ which one to display. columns: (auto, 1fr, auto), output, repeat([#h(0.25em) . #h(0.25em)]), - entry.pages, + entry.pages.join(", "), ) ) } diff --git a/glossy/tests/format-term/test.typ b/glossy/tests/format-term/test.typ index c38ed49..9de52a5 100644 --- a/glossy/tests/format-term/test.typ +++ b/glossy/tests/format-term/test.typ @@ -32,7 +32,7 @@ [@html:short], [@html:long], [@html:both], [@css:short], [@css:long], [@css:both], - [@tps:short], [@tps:long], [@tps:both], + [@tps:short], [@tps:long], [@tps:both:nouse], [@concise:short], [@concise:long], [@concise:both], [@shortonly:short], [@shortonly:long], [@shortonly:both], @@ -40,7 +40,7 @@ [@html:short:cap], [@html:long:cap], [@html:both:cap], [@a:css:short], [@a:css:long], [@a:css:both], - [@tps:short:pl], [@tps:long:pl], [@tps:both:pl], + [@tps:short:pl], [@tps:long:pl], [@tps:both:nouse:pl], [@concise:a:cap:short], [@concise:a:cap:long], [@concise:a:cap:both], [@an:shortonly:short], [@an:shortonly:long], [@an:shortonly:both], ) diff --git a/glossy/tests/hidden-terms/test.typ b/glossy/tests/hidden-terms/test.typ index ffbaf3a..49a4542 100644 --- a/glossy/tests/hidden-terms/test.typ +++ b/glossy/tests/hidden-terms/test.typ @@ -24,9 +24,9 @@ = Hiding terms -Here's a hidden term: @html:hide +Here's a hidden term: @html[] -Here's another hidden term: @css:hidden +Here's another hidden term: @css[] And here's a shown term: @tps diff --git a/glossy/tests/override-display/ref/1.png b/glossy/tests/override-display/ref/1.png index d077037..144db79 100644 Binary files a/glossy/tests/override-display/ref/1.png and b/glossy/tests/override-display/ref/1.png differ diff --git a/glossy/tests/refs-exhaustive/ref/1.png b/glossy/tests/refs-exhaustive/ref/1.png index d7e6746..23cd59a 100644 Binary files a/glossy/tests/refs-exhaustive/ref/1.png and b/glossy/tests/refs-exhaustive/ref/1.png differ diff --git a/glossy/tests/refs-exhaustive/ref/2.png b/glossy/tests/refs-exhaustive/ref/2.png index 7898d3b..299fe65 100644 Binary files a/glossy/tests/refs-exhaustive/ref/2.png and b/glossy/tests/refs-exhaustive/ref/2.png differ diff --git a/glossy/tests/refs-exhaustive/ref/3.png b/glossy/tests/refs-exhaustive/ref/3.png index d7dbaf7..fb83da5 100644 Binary files a/glossy/tests/refs-exhaustive/ref/3.png and b/glossy/tests/refs-exhaustive/ref/3.png differ diff --git a/glossy/tests/refs-exhaustive/ref/4.png b/glossy/tests/refs-exhaustive/ref/4.png index 13d9c6e..e4831bb 100644 Binary files a/glossy/tests/refs-exhaustive/ref/4.png and b/glossy/tests/refs-exhaustive/ref/4.png differ diff --git a/glossy/tests/refs-exhaustive/ref/5.png b/glossy/tests/refs-exhaustive/ref/5.png index 160a403..03be655 100644 Binary files a/glossy/tests/refs-exhaustive/ref/5.png and b/glossy/tests/refs-exhaustive/ref/5.png differ diff --git a/glossy/tests/refs-exhaustive/ref/6.png b/glossy/tests/refs-exhaustive/ref/6.png deleted file mode 100644 index fcbc89b..0000000 Binary files a/glossy/tests/refs-exhaustive/ref/6.png and /dev/null differ diff --git a/glossy/tests/refs-exhaustive/test.typ b/glossy/tests/refs-exhaustive/test.typ index ca19418..54cc138 100644 --- a/glossy/tests/refs-exhaustive/test.typ +++ b/glossy/tests/refs-exhaustive/test.typ @@ -293,14 +293,25 @@ This document tries all reference forms and modifiers on a large set of terms. == Conflicting multiple modifiers combined (short, long, both together) -- @tps:short:long -- @tps:short:both -- @tps:long:both -- @tps:short:long:both -- @html:both:long:short -- @doc:long:short -- @cpu:both:long:short -- @api:short:long:both +Assert that a panic occurs. +#import "/src/gloss.typ": __gls, __default-format-term +#let manual-gls(key, mods) = __gls(key, modes-modifiers: mods, format-term: __default-format-term, show-term: (body) => [#strong(body)], term-links: false, display-text: none) +//- @tps:short:long +#assert-panic(manual-gls.with("tps", ("short", "long"))) +//- @tps:short:both +#assert-panic(manual-gls.with("tps", ("short", "both"))) +//- @tps:long:both +#assert-panic(manual-gls.with("tps", ("long", "both"))) +//- @tps:short:long:both +#assert-panic(manual-gls.with("tps", ("short", "long", "both"))) +//- @html:both:long:short +#assert-panic(manual-gls.with("html", ("both", "long", "short"))) +//- @doc:long:short +#assert-panic(manual-gls.with("doc", ("long", "short"))) +//- @cpu:both:long:short +#assert-panic(manual-gls.with("cpu", ("both", "long", "short"))) +//- @api:short:long:both +#assert-panic(manual-gls.with("api", ("short", "long", "both"))) == Combining capitalization and plural with short/long/both diff --git a/glossy/tests/refs-in-captions/ref/1.png b/glossy/tests/refs-in-captions/ref/1.png index 8b819b6..06290d9 100644 Binary files a/glossy/tests/refs-in-captions/ref/1.png and b/glossy/tests/refs-in-captions/ref/1.png differ diff --git a/glossy/tests/refs-in-captions/test.typ b/glossy/tests/refs-in-captions/test.typ index 7f2cc5e..e5ecc84 100644 --- a/glossy/tests/refs-in-captions/test.typ +++ b/glossy/tests/refs-in-captions/test.typ @@ -37,7 +37,7 @@ === `@ShortInHeader:short` = "@ShortInHeader:short" === `@LongInHeader:long` = "@LongInHeader:long" -=== `@BothInHeader:both` = "@BothInHeader:both" +=== `@BothInHeader:both:nouse` = "@BothInHeader:both:nouse" === `@NormalInHeader` = "@NormalInHeader" #line(length: 100%) @@ -47,7 +47,7 @@ #figure([], caption: [`@ShortInCaption:short` = "@ShortInCaption:short"]) #figure([], caption: [`@LongInCaption:long` = "@LongInCaption:long"]) -#figure([], caption: [`@BothInCaption:both` = "@BothInCaption:both"]) +#figure([], caption: [`@BothInCaption:both:nospend` = "@BothInCaption:both:nospend"]) #figure([], caption: [`@NormalInCaption` = "@NormalInCaption"]) #line(length: 100%)