Merge pull request #55 from th1j5/main
Formalize & document glossy behaviour, simplify internals and add some features
No files matched your search
@@ -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` <br> `: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` <br> `: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`<br>`a:`<br>`:an`<br>`an:` | Include the article (`an` is just an alias of `a`, they're equivalent) |
|
||||
| `:use` <br> `: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` <br> `: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.
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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(", "))
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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(", "))]
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
columns: (1fr, auto),
|
||||
gutter: 1em,
|
||||
[#short-display#entry.label#long-display#description#entry.label],
|
||||
[#entry.pages]
|
||||
[#entry.pages.join(", ")]
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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(", ")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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(", "))
|
||||
}
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
},
|
||||
|
||||
|
Before Width: | Height: | Size: 490 KiB After Width: | Height: | Size: 104 KiB |
@@ -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(", "),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 882 KiB After Width: | Height: | Size: 187 KiB |
@@ -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(", "),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 275 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 502 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 534 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 376 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 407 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 78 KiB |
@@ -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
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 652 KiB After Width: | Height: | Size: 135 KiB |
@@ -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%)
|
||||
|
||||