From 54680f2ed3d0c71a6bb33d8e3e731f3440b4023b Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Tue, 22 Oct 2024 09:49:26 -0600 Subject: [PATCH] feat(glossy): initial commit of glossy --- glossy/0.1.0/LICENSE | 21 ++ glossy/0.1.0/README.md | 198 +++++++++++++++++++ glossy/0.1.0/examples/readme.pdf | Bin 0 -> 21395 bytes glossy/0.1.0/examples/readme.typ | 63 ++++++ glossy/0.1.0/lib.typ | 1 + glossy/0.1.0/src/gloss.typ | 317 +++++++++++++++++++++++++++++++ glossy/0.1.0/src/themes.typ | 47 +++++ glossy/0.1.0/src/utils.typ | 19 ++ glossy/0.1.0/thumbnail.png | Bin 0 -> 44135 bytes glossy/0.1.0/typst.toml | 11 ++ 10 files changed, 677 insertions(+) create mode 100644 glossy/0.1.0/LICENSE create mode 100644 glossy/0.1.0/README.md create mode 100644 glossy/0.1.0/examples/readme.pdf create mode 100644 glossy/0.1.0/examples/readme.typ create mode 100644 glossy/0.1.0/lib.typ create mode 100644 glossy/0.1.0/src/gloss.typ create mode 100644 glossy/0.1.0/src/themes.typ create mode 100644 glossy/0.1.0/src/utils.typ create mode 100644 glossy/0.1.0/thumbnail.png create mode 100644 glossy/0.1.0/typst.toml diff --git a/glossy/0.1.0/LICENSE b/glossy/0.1.0/LICENSE new file mode 100644 index 0000000..35d39a7 --- /dev/null +++ b/glossy/0.1.0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Stephen Waits + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/glossy/0.1.0/README.md b/glossy/0.1.0/README.md new file mode 100644 index 0000000..de8eae8 --- /dev/null +++ b/glossy/0.1.0/README.md @@ -0,0 +1,198 @@ +# Glossy + +This package provides utilities to manage and render glossaries within +documents. It includes functions to define and use glossary terms, track their +usage, and generate a glossary list with references to where terms are used in +the document. + +![image of sample output](thumbnail.png) + +## Motivation + +Glossy is heavily inspired by +[glossarium](https://typst.app/universe/package/glossarium), with a few key +different goals: + +1. Provide a simple interface which allows for complete control over glossary + display. To do this, `glossy`'s `#glossary()` function accepts a theme + parameter. The goal here is to separate presentation and logic. +1. Simplify the user interface as much as possible. Glossy has exactly two + exports, `init-glossary` and `glossary`. +1. Double-down on `glossy`'s excellent `@term` reference approach, completely + eliminating the need to make any calls to `gls()` and friends. +1. Mimic established patterns and best practices. For example, `glossy`'s + `#glossary()` function is intentionally similar (in naming and parameters) to + `typst`'s built-int `#bibliography`, to the degree possible. +1. Simplify the implementation. The `glossy` code is significantly shorter and + easier to understand. + +## Features + +- Define glossary terms with short and long forms, descriptions, and grouping. +- Automatically tracks term usage in the document, making references simple + through @labels. +- Supports modifiers to adjust how terms are displayed (capitalize, pluralize, + etc.). +- Generates a formatted glossary section with backlinks to term occurrences in + the document. +- Easily customizable themes for rendering glossary sections, groups, and + entries. + +## Usage + +### Import the package + +```typst +#import "@preview/glossy:0.1.0": * + +``` + +### Defining Glossary Terms + +Use the `init-glossary` function to initialize glossary entries and process +document content: + +```typst +#let myGlossary = ( + ( + key: "html", + short: "HTML", + long: "Hypertext Markup Language", + description: "A standard language for creating web pages", + group: "Web" + ), + ( + key: "css", + short: "CSS", + long: "Cascading Style Sheets", + description: "A stylesheet language used for describing the presentation of a document", + group: "Web" + ), + ( + key: "tps", + short: "TPS", + long: "test procedure specification" + ), + // Add more entries as needed +) + +#show: init-glossary.with(myGlossary) + +// Your document content here, where glossary terms are used. +``` + +Note that you could also load glossary entries from a data file using +[#yaml()](https://typst.app/docs/reference/data-loading/yaml/), +[#json()](https://typst.app/docs/reference/data-loading/json/), or +[similar](https://typst.app/docs/reference/data-loading/). + +### Using Glossary Terms in Your Document + +Use glossary terms within your text using the `@reference` functionality in +typst. + +For example, `@html` and `@css` are used in the following text: + +```typst +In modern web development, languages like @html and @css are essential. + +Now make sure I get your @tps:short reports by 2pm! +``` + +... which should output: + +```text +In modern web development, languages like Hypertext Markup Language (HTML) and +Cascading Style Sheets (CSS) are essential. + +Now make sure I get your TPS reports by 2pm! +``` + +You can control how each term is displayed using the following modifiers: + +- **cap**: Capitalizes the term. +- **pl**: Uses the plural form of the term. +- **both**: Displays both the long and short forms (e.g., "Long Form (Short Form)"). +- **short**: Displays only the short form. +- **long**: Displays only the long form. + +Each modifier is separated by a colon (':') character. Examples: + +| **Input** | **Output** | +| ----------------------- | -------------------------------------------- | +| `@tps` (first use) | "technical procedure specification (TPS)" | +| `@tps` (subsequent use) | "TPS" | +| `@tps:short` | "TPS" | +| `@tps:long` | "technical procedure specification (TPS)" | +| `@tps:both` | "technical procedure specification (TPS)" | +| `@tps:long:cap` | "Technical procedure specification" | +| `@tps:long:pl` | "technical procedure specifications" | +| `@tps:short:pl` | "TPSes" | +| `@tps:both:pl:cap` | "Technical procedure specifications (TPSes)" | + +Combine modifiers in any way you like. But know that if you combine any of +`short`, `long`, and/or `both`, only one will be used. + +### Generating the Glossary + +To display the glossary in your document, use the `glossary` function. Customize +the title and specify a theme if desired: + +```typst +#glossary(title: "Web Development Glossary", theme: my-theme, groups: ("Web")) +``` + +### Themes + +Customize the appearance of the glossary using themes. Each theme defines how +sections, groups, and entries are displayed. + +- **section**: This is a main glossary section. It contains multiple glossary groups. +- **group**: A group within the main glossary section. Contains multiple + entries. Can be an empty string `""` or `none`. +- **entry**: A single entry within a group. This is the actual term, and can + include any of the fields in entry. Contains the following fields: + - `key`: The term key. (always present and unique) + - `short`: The short form of the term. (always present) + - `long`: The long form. (can be `none`) + - `description`: The description or definition. (can be `none`) + - `pages`: Pages with links to term usage in doc. (always present) + +Example of a minimal theme: + +```typst +#let my-theme = ( + section: (title, body) => { + heading(level: 1, title) + body + }, + group: (name, body) => { + if name != none and name != "" { + heading(level: 2, name) + } + body + }, + entry: (entry, i, n) => { + // i is the entry's index, n is total number of entries + let output = [#entry.short] + if entry.long != none { + output = [#output -- #entry.long] + } + if entry.description != none { + output = [#output: #entry.description] + } + block( + grid( + columns: (auto, 1fr, auto), + output, + repeat([#h(0.25em) . #h(0.25em)]), + entry.pages, + ) + ) + } +) +``` + +## License + +This project is licensed under the MIT License. diff --git a/glossy/0.1.0/examples/readme.pdf b/glossy/0.1.0/examples/readme.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6f72eeb2a67bda9de7d59bbeff3bf077dcd11243 GIT binary patch literal 21395 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 { + heading(level: 1, title) + body + }, + group: (name, body) => { + if name != none and name != "" { + heading(level: 2, name) + } + body + }, + entry: (entry, i, n) => { + // i is the entry's index, n is total number of entries + let output = [#entry.short] + if entry.long != none { + output = [#output -- #entry.long] + } + if entry.description != none { + output = [#output: #entry.description] + } + block( + grid( + columns: (auto, 1fr, auto), + output, + repeat([#h(0.25em) . #h(0.25em)]), + entry.pages, + ) + ) + } +) + +#glossary(theme: my-theme) diff --git a/glossy/0.1.0/lib.typ b/glossy/0.1.0/lib.typ new file mode 100644 index 0000000..f50143e --- /dev/null +++ b/glossy/0.1.0/lib.typ @@ -0,0 +1 @@ +#import "src/gloss.typ": init-glossary, glossary diff --git a/glossy/0.1.0/src/gloss.typ b/glossy/0.1.0/src/gloss.typ new file mode 100644 index 0000000..5c9a93d --- /dev/null +++ b/glossy/0.1.0/src/gloss.typ @@ -0,0 +1,317 @@ +#import "./themes.typ": * +#import "./utils.typ": * + +#let __gloss_entries = state("__gloss_entries", (:)) +#let __gloss_used = state("__gloss_used", (:)) + +#let __gloss_label_prefix = "__gloss:" + +// given an array of dictionaries, make sure each has all the keys we'll +// reference, using default values if needed +#let __normalize_entries(entry-list) = { + let new-list = () + for entry in entry-list { + let long = entry.at("long", default: none) + let longplural = entry.at("longplural", default: none) + if long != none and longplural == none { + longplural = __pluralize(long) + } + + new-list.push(( + key: entry.key, + short: entry.short, + plural: entry.at("plural", default: __pluralize(entry.short)), + long: long, + longplural: longplural, + description: entry.at("description", default: none), + group: entry.at("group", default: ""), + )) + } + return new-list +} + +// update our state with a glossary entry +#let __add_entry(entry) = { + // make sure our final glossary state does not already have this key + let has_key = __gloss_entries.final().at(entry.key, default: false) + if has_key == true { + panic("Glossary error. Duplicate key: " + entry.key) + } + + // add it to the state + __gloss_entries.update(st => { + st.insert(entry.key, entry) + return st + }) +} + +// fetch a glossary entry from our state, or panic +#let __get_entry(key) = { + let entries = __gloss_entries.final() + if key not in entries { + panic("Glossary error. Missing key: " + key) + } + + entries.at(key) +} + +// returns true if an entry with `key` is in our glossary state +#let __has_entry(key) = { + let entries = __gloss_entries.final() + key in entries +} + +// helper to prefix a key for label/reference scoping for the dictionary entry +#let __dict_label_str(key) = { + __gloss_label_prefix + key +} + +// helper to get a label with prefix for the dictionary entry +#let __dict_label(key) = { + label(__dict_label_str(key)) +} + +// helper to prefix a key for label/reference scoping for the term use in a doc +#let __term_label_str(key, index) = { + __gloss_label_prefix + key + "." + str(index) +} + +// helper to get a label with prefix for the term use in a doc +#let __term_label(key, index) = { + label(__term_label_str(key, index)) +} + +// update the usage count for a given key +#let __save_count(key, count) = { + __gloss_used.update(st => { + st.insert(key, count) + return st; + }) +} + +// the main function which emits terms used in a document +// +// handles all the modifiers: +// +// - cap: capitalize the term +// - pl: pluralize the term +// - both: emit "Long form (short form)" +// - short: emit just the short form +// - long: emit just the long form +#let __gls( + key, + modifiers: array +) = context { + + // Get the term + let entry = __get_entry(key) + let entry_label = __dict_label_str(key) + + // Lookup and increment the count + let entry_counter = counter(entry_label) + entry_counter.step() + + // See if this is the first use + let key_index = entry_counter.get().first() + let first = key_index == 0 + + // Count the entry as used so we can link back from glossary + __save_count(entry.key, key_index + 1) + + // Helper: Apply pluralization if needed + let pluralize_term = (singular, plural) => { + if "pl" in modifiers and plural != none { plural } else { singular } + } + + // Helper: Capitalize term if "cap" modifier present + let capitalize_term = (term) => { + if "cap" in modifiers { upper(term.first()) + term.slice(1) } else { term } + } + + // Helper: Select and format the displayed term (long, short, or both) + let select_term = (is_long_mode, use_both) => { + // Derive pluralized and capitalized versions of the long and short forms + let long_form = capitalize_term(pluralize_term(entry.long, entry.longplural)) + let short_form = capitalize_term(pluralize_term(entry.short, entry.plural)) + + // If "both" modifier is present, show both the long form and short form + if use_both { + // If the long form exists, show "Long Form (Short Form)" + if long_form != none { + [#long_form (#short_form)] + } + // Fallback: If no long form exists, just show the short form + else { + [#short_form] + } + } + // If in "long mode", show the long form, fallback to short if long form is missing + else if is_long_mode { + if long_form != none { + [#long_form] + } + // Fallback: if no long form, show short + else { + [#short_form] + } + } + // Default case: Just show the short form + else { + [#short_form] + } + } + + // Determine which form to display: "short", "long", or "both" + let is_both = "both" in modifiers + let is_long = "long" in modifiers and not is_both + let is_short = "short" in modifiers and not is_both and not is_long + + // Final display logic + let display = if is_both or is_long or is_short { + // User requested specific behavior via modifiers + select_term(is_long, is_both) + } else { + // Default behavior: show "both" on first use, else "short" + select_term(false, first) + } + + // TODO: figure out how to link to __dict_label(key) if the glossary exists + // NOTE: this is a low priority, I don't think it's that important or useful. + + // Emit with labels for this instance of the term usage + [#display#metadata(display)#__term_label(key, key_index)] +} + +// Create all the backlinks to term uses in a doc, in the form of page numbers +// linked. Used when emitting a glossary, so its page numbers can link back to +// the uses. +#let __create_backlinks(key, count) = { + // create array of labels to link to + let labels = for i in range(count) { + (__term_label(key, i),) + } + + // create arrays of locations, and page number display text + let pages = labels + .map(l => { locate(l) }) + .map((loc) => { numbering(__default(loc.page-numbering(), "1"), loc.page()) }) + + // convert labels to links, filtering out duplicated pages + let seen = () + let links = for i in range(labels.len()) { + let l = labels.at(i) + let p = pages.at(i) + if seen.contains(p) { + (none,) + } else { + seen.push(p) + (link(l, p),) + } + }.filter(l => l != none) + + // connect links with commas and return + links.join(", ") +} + +// Main wrapper (usually used in a `#show: init-glossary`) which loads the +// entries passed in into our state. Furthermore, hooks into references so that +// we can intercept term usage in a doc and label them appropriately. +#let init-glossary(entries, body) = context { + for entry in __normalize_entries(entries) { + __add_entry(entry) + } + + // convert refs we recognize into links with labels + show ref: r => { + let (key, ..modifiers) = str(r.target).split(":") + if __has_entry(key) { + __gls(key, modifiers: modifiers) + } else { + r + } + } + + body +} + +// Used to print a glossary. Can customize title, theme, and/or specify groups. +// +// A theme is a dictionary with three attributes: +// +// #let my-theme = ( +// section: (title, body) => { +// // how to display the glossary section and its body which will contain +// // groups, each with their entries +// }, +// +// group: (name, body) => { +// // how to display a group name and its body (which will contain the +// // entries in that group) +// }, +// +// entry: (entry, i, n) => { +// // how to display a single entry, along with its index and total count +// } +// } + +#let glossary(title: "Glossary", theme: theme-2col, groups: ()) = context { + // our output is a map of group name to array of entries (each entry is a map) + let output = (:) + + // pull in entire dictionary + let all_entries = __gloss_entries.final() + + // filter down to just what we used + let all_used = __gloss_used.final() + + // TODO: what about entries with no group? Or group == "" or == none + + // get all groups + let all_groups = all_entries.values().map(e => e.at("group")).dedup().sorted() + let groups = if groups.len() == 0 { all_groups } else { groups } + + // make sure requested groups are legit + for g in groups { + if g not in all_groups { + panic("Requested group not found: " + g) + } + } + + // iterate one group at a time + for g in groups { + // collect used entries in this group + let cur = () + for (key, count) in all_used { + let e = all_entries.at(key) + if e.at("group") == g { + // this term is both in this group and was used, add to our output + let short = e.at("short") + let long = e.at("long") + let description = e.at("description") + let label = [#metadata(key)#__dict_label(key)] + let pages = __create_backlinks(key, count) + cur.push((short: short, long: long, description: description, label: label, pages: pages)) + } + } + if cur.len() > 0 { + output.insert(g, cur.sorted(key: e => e.short)) + } + } + + // TODO: rendering for a) just one group, or b) the default group (ie terms + // with no group)?? -- in this case should we just not use the theme.group() + // function? + + // render it using our theme + (theme.section)( + title, + for (group, entries) in output { + (theme.group)( + group, + for (i,e) in entries.enumerate() { + (theme.entry)(e, i, entries.len()) + }, + ) + } + ) +} diff --git a/glossy/0.1.0/src/themes.typ b/glossy/0.1.0/src/themes.typ new file mode 100644 index 0000000..3cadd63 --- /dev/null +++ b/glossy/0.1.0/src/themes.typ @@ -0,0 +1,47 @@ +#let theme-2col = ( + section: (title, body) => { + set par.line(numbering: none) + heading(level: 1, title) + columns(2, body) + }, + + group: (name, body) => { + if name != none { + heading(level: 2, name) + } + body + }, + + entry: (entry, i, n) => { + // build our title, etc + let short-display = text(weight: "regular", entry.short) + let long-display = if entry.long == none { + [] + } else { + [#h(0.25em) -- #entry.long] + } + + // build our description + let description = if entry.description == none { + [] + } else { + [: #entry.description] + } + + text( + size: 0.75em, + weight: "light", + grid( + columns: (auto,auto,auto,1fr,1em,auto), + align: (left, left, left, center, center, right), + [#short-display#entry.label], + [#long-display], + [#description], + [#repeat(h(0.25em) + "." + h(0.25em))], + [ . ], + [#entry.pages] + ) + ) + }, +) + diff --git a/glossy/0.1.0/src/utils.typ b/glossy/0.1.0/src/utils.typ new file mode 100644 index 0000000..9bc45bc --- /dev/null +++ b/glossy/0.1.0/src/utils.typ @@ -0,0 +1,19 @@ +#let __default(val, default) = if val == none { default } else { val } + +#let __pluralize(word) = { + // Define helper functions for checking endings + let ends_with = (suffix) => lower(word).ends-with(suffix) + let ends_with_any = (suffixes) => suffixes.any((suffix) => ends_with(suffix)) + + if ends_with_any(( "s", "x", "z", "sh", "ch" )) { // Handle special cases for "es" endings + word + "es" + } else if ends_with("y") and not ends_with_any(( "a", "e", "i", "o", "u" )) { // Handle "y" -> "ies" + word.slice(0, -1) + "ies" + } else if ends_with("f") { // Handle "f" or "fe" -> "ves" + word.slice(0, -1) + "ves" + } else if ends_with("fe") { + word.slice(0, -2) + "ves" + } else { // Default to adding "s" + word + "s" + } +} diff --git a/glossy/0.1.0/thumbnail.png b/glossy/0.1.0/thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..b93bf7cdd08213c2538497e5abffb9d40ee62b5f GIT binary patch literal 44135 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 n@)iH->3<`6{=dU}@s#e7`SlB0f>)4dXqTxW"] +license = "MIT" +description = "A very simple glossary system with easily customizable output." +categories = ["model"] +keywords = ["glossary", "index", "dictionary", "gloss"] +repository = "https://github.com/swaits/typst-collection" +exclude = ["thumbnail.png", "examples/readme.pdf"]