chore(glossy): format with typestyle

This commit is contained in:
swaits committed 2025-10-26 16:00:23 -06:00
1 parent 6d83a858c8
commit 022892df88
25 files changed
+345 -209

No files matched your search

+1 -1
View File
@@ -1,2 +1,2 @@
#import "src/gloss.typ": init-glossary, glossary
#import "src/gloss.typ": glossary, init-glossary
#import "src/themes.typ": *
+88 -27
View File
@@ -59,7 +59,7 @@
longplural: entry.at("longplural", default: __pluralize(long)),
longarticle: entry.at("longarticle", default: __determine_article(long)),
description: entry.at("description", default: none),
group: entry.at("group", default: "")
group: entry.at("group", default: ""),
)
}
@@ -149,7 +149,9 @@
counter(__gloss_label_prefix + key).step()
}
if count-as-first-use {
counter(__gloss_label_prefix + key + __gloss_first_use_counter_postfix).step()
counter(
__gloss_label_prefix + key + __gloss_first_use_counter_postfix,
).step()
}
}
@@ -181,7 +183,9 @@
// boolean: If the entry is used above the location in the document
//
#let __is_term_first_used(key, location: none) = {
let c = counter(__gloss_label_prefix + key + __gloss_first_use_counter_postfix)
let c = counter(
__gloss_label_prefix + key + __gloss_first_use_counter_postfix,
)
c = if location == none { c.final() } else { c.at(location) }
c.at(0) > 0
}
@@ -195,7 +199,9 @@
// 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)
counter(
__gloss_label_prefix + key + __gloss_first_use_counter_postfix,
).update(0)
}
// Determine if the glossary contains a visible entry
@@ -236,8 +242,23 @@
// content: The fully formatted term, including optional article, capitalization,
// usage tracking metadata, and the chosen form (short, long, or both).
//
#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")
#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",
)
// ---------------------------------------------------------------------------
// Normalize mode & modifier inputs AND check if the modifiers are valid
// ---------------------------------------------------------------------------
@@ -255,7 +276,10 @@
} 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.")
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.")
}
@@ -269,7 +293,8 @@
// 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 => {
let (requested_modes, modifiers) = modes-modifiers
.sorted(key: it => {
if it in possible_modes {
// It is a 'mode'
return -1
@@ -279,7 +304,8 @@
// It is a 'modifier'
return 1
}
}).split("__MIDDLE_ITEM")
})
.split("__MIDDLE_ITEM")
// ---------------------------------------------------------------------------
// Check for illegal mode and/or modifier combinations
@@ -335,12 +361,18 @@
// 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 default-count-as-first-use = (requested_mode == "auto" or requested_mode == "both")
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))
__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
@@ -376,7 +408,7 @@
// - 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 get_article = mode => {
let wants_article = "an" in modifiers
if not wants_article {
none
@@ -406,7 +438,10 @@
}
let long_available = entry.long != none
let mode = if (requested_mode == "both" or requested_mode == "long") and not long_available {
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 {
@@ -481,9 +516,13 @@
//
#let __create_backlinks(key) = {
return query(__term_label(key)) // find all reference
.map(meta => { // extract location and page number (or symbol)
.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))
let page = numbering(
__default(loc.page-numbering(), "1"),
..counter(page).at(loc),
)
(loc, page)
})
.dedup(key: ((loc, page)) => page) // deduplicate by page
@@ -559,17 +598,23 @@
format-term: __default-format-term,
show-term: __default-show-term,
term-links: false,
body
body,
) = context {
// Type checking
let checked-entries = (:)
for (key, entry) in entries {
let checked-key = z.parse(key, z.string(), scope: ("dictionary key",))
let checked-entry = z.parse(entry, dict-schema, scope: ("dictionary entry",))
let checked-entry = z.parse(entry, dict-schema, scope: (
"dictionary entry",
))
checked-entries.insert(checked-key, checked-entry)
}
let checked-format-term = z.parse(show-term, z.function(), scope: ("format-term",))
let checked-show-term = z.parse(show-term, z.function(), scope: ("show-term",))
let checked-format-term = z.parse(show-term, z.function(), scope: (
"format-term",
))
let checked-show-term = z.parse(show-term, z.function(), scope: (
"show-term",
))
let checked-body = z.parse(body, z.content(), scope: ("body",))
// Process and store each glossary entry
@@ -590,7 +635,12 @@
// - That "key" does not correspond to an actual entry,
// - There is at least one modifier,
// - The first modifier corresponds to an existing entry key.
let can_swap = (lower(raw_key) == "a" or lower(raw_key) == "an") and not __has_entry(raw_key) and raw_modifiers.len() > 0 and __has_entry(raw_modifiers.first())
let can_swap = (
(lower(raw_key) == "a" or lower(raw_key) == "an")
and not __has_entry(raw_key)
and raw_modifiers.len() > 0
and __has_entry(raw_modifiers.first())
)
// If we can swap, use the first modifier as the key and insert "a" as the first modifier.
let (key, modifiers) = if can_swap {
@@ -602,7 +652,14 @@
// Now see if this is an actual glossary term key
if __has_entry(key) {
// Found in dictionary, render via __gls()
__gls(key, modes-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
@@ -658,7 +715,9 @@
// Type checking
let checked-title = z.parse(title, z.content(), scope: ("title",))
let checked-groups = z.parse(groups, groups-list-schema, scope: ("groups",))
let checked-ignore-case = z.parse(ignore-case, z.boolean(), scope: ("ignore-case",))
let checked-ignore-case = z.parse(ignore-case, z.boolean(), scope: (
"ignore-case",
))
let checked-theme = z.parse(theme, theme-schema, scope: ("theme",))
// Collect and organize entries by group
@@ -706,7 +765,7 @@
long: entry.at("long"),
description: entry.at("description"),
label: [#metadata(key)#__entry_label(key)],
pages: __create_backlinks(key)
pages: __create_backlinks(key),
))
}
}
@@ -719,7 +778,9 @@
let sorted_entries = if sort {
current_entries
// 1. create array of tuples with (lower [if ignore-case], entry)
.map(e => { if ignore-case { (lower(e.short), e) } else { (e.short, e) } })
.map(e => {
if ignore-case { (lower(e.short), e) } else { (e.short, e) }
})
// 2. sort the tuples (by first element then second)
.sorted() // NOTE: sorted() is NOT language-aware
// 3. strip away the tuple's first element, leaving an array of entries
@@ -747,10 +808,10 @@
output.len(),
for (i, entry) in entries.enumerate() {
(checked-theme.entry)(entry, i, entries.len())
}
},
)
group_index += 1
}
},
)
]
}
+3 -3
View File
@@ -10,8 +10,8 @@
longplural: z.content(optional: true),
description: z.content(optional: true),
group: z.content(optional: true),
)
)
),
),
)
#let theme-schema = z.dictionary(
@@ -19,7 +19,7 @@
section: z.function(),
group: z.function(),
entry: z.function(),
)
),
)
#let groups-list-schema = z.array(pre-transform: z.coerce.array)
+3 -5
View File
@@ -5,7 +5,6 @@
v(1em)
body
},
group: (name, index, total, body) => {
if name != "" and total > 1 {
v(1.5em)
@@ -16,7 +15,6 @@
}
body
},
entry: (entry, index, total) => {
let short-display = text(weight: "bold", entry.short)
let long-display = if entry.long == none {
@@ -40,10 +38,10 @@
columns: (1fr, auto),
gutter: 0.75em,
[#short-display#long-display#description#entry.label],
text(fill: rgb("#666666"), entry.pages.join(", "))
)
}
text(fill: rgb("#666666"), entry.pages.join(", ")),
)
},
),
)
},
)
+3 -5
View File
@@ -15,7 +15,6 @@
heading(level: 1, title)
body
},
// Renders a group of related glossary terms
// Parameters:
// name: Group name (empty string for ungrouped terms)
@@ -28,7 +27,6 @@
}
body
},
// Renders a single glossary entry with term, definition, and page references
// Parameters:
// entry: Dictionary containing term data:
@@ -62,9 +60,9 @@
left: 1em,
bottom: 0.5em,
block(
[#term#entry.label#long-form#description #h(1em) (pp. #entry.pages.join(", "))]
)
)
[#term#entry.label#long-form#description #h(1em) (pp. #entry.pages.join(", "))],
),
),
)
},
)
+3 -5
View File
@@ -5,7 +5,6 @@
heading(level: 1, smallcaps(title))
body
},
group: (name, index, total, body) => {
if name != "" and total > 1 {
v(1em)
@@ -14,7 +13,6 @@
}
body
},
entry: (entry, index, total) => {
let short-display = text(weight: "regular", entry.short)
let long-display = if entry.long == none {
@@ -38,10 +36,10 @@
columns: (1fr, auto),
gutter: 1em,
[#short-display#entry.label#long-display#description#entry.label],
[#entry.pages.join(", ")]
)
}
[#entry.pages.join(", ")],
)
},
),
)
},
)
+6 -8
View File
@@ -17,7 +17,6 @@
heading(level: 1, outlined: false, text(weight: "bold", size: 1.1em, title))
body
},
// Renders a group of related glossary terms
// Parameters:
// name: Group name (empty string for ungrouped terms)
@@ -30,13 +29,12 @@
spacing: 0.5em,
pad(
top: 0.5em,
text(weight: "bold", size: 0.9em, name)
)
text(weight: "bold", size: 0.9em, name),
),
)
}
body
},
// Renders a single glossary entry with term, definition, and page references
// Parameters:
// entry: Dictionary containing term data:
@@ -53,7 +51,7 @@
size: 0.65em,
weight: "medium",
fill: gray.darken(60%),
entry.short
entry.short,
)
let long-form = if entry.long == none {
@@ -83,9 +81,9 @@
text(
size: 0.6em,
fill: gray.darken(20%),
entry.pages.join(", ")
)
)
entry.pages.join(", "),
),
),
)
},
)
+10 -6
View File
@@ -4,13 +4,13 @@
heading(level: 1, title)
body
},
group: (name, index, total, body) => {
if name != "" and total > 1 {
heading(level: 2, name)
}
table(columns: 4,
table(
columns: 4,
stroke: none,
inset: (x, y) => {
if (x == 0) {
@@ -22,11 +22,15 @@
}
},
table.header([*Abbreviation*], [*Full Name*], [*Description*], [*Pages*]),
..body
..body,
)
},
entry: (entry, index, total) => {
(entry.short + entry.label, entry.long, entry.description, entry.pages.join(", "))
}
(
entry.short + entry.label,
entry.long,
entry.description,
entry.pages.join(", "),
)
},
)
+9 -7
View File
@@ -15,7 +15,6 @@
heading(level: 1, title)
columns(2, body)
},
// Renders a group of related glossary terms
// Parameters:
// name: Group name (empty string for ungrouped terms)
@@ -28,7 +27,6 @@
}
body
},
// Renders a single glossary entry with term, definition, and page references
// Parameters:
// entry: Dictionary containing term data:
@@ -62,11 +60,15 @@
grid(
columns: (auto, 1fr, 1em, auto),
align: (left, center, center, right),
[#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.join(", ")] // Page references
)
[#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.join(", ")],
// Page references
),
)
},
)
+14 -3
View File
@@ -16,8 +16,8 @@
}
// 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))
let ends_with = suffix => lower(word).ends-with(suffix)
let ends_with_any = suffixes => suffixes.any(suffix => ends_with(suffix))
// Map of irregular plurals for common words
let irregulars = (
@@ -80,7 +80,18 @@
// Irregular suffixes
let f_to_ves = ("leaf", "loaf", "calf", "half", "wolf", "thief")
let fe_to_ves = ("wife", "knife", "life")
let o_to_os = ("photo", "piano", "halo", "radio", "video", "studio", "solo", "taco", "memo", "zero")
let o_to_os = (
"photo",
"piano",
"halo",
"radio",
"video",
"studio",
"solo",
"taco",
"memo",
"zero",
)
// Convenience
let w = lower(word)
+38 -17
View File
@@ -6,10 +6,11 @@
tps: (
short: "TPS",
long: "test procedure specification",
description: "A document on how to run all the test procedures"),
description: "A document on how to run all the test procedures",
),
)
#show: init-glossary.with(myGlossary, show-term: (body) => [#emph(body)])
#show: init-glossary.with(myGlossary, show-term: body => [#emph(body)])
#set heading(numbering: "1.1")
#set page(numbering: "1")
@@ -43,8 +44,15 @@ Now make sure I get your @tps:short reports by 2pm!
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)
#import "/src/gloss.typ": __default-format-term, __gls
#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.\""
@@ -55,14 +63,29 @@ These modifiers are semantically mutually exclusive. When multiple are used,
columns: 2,
table.header([*Input*], [*Error*]),
[`@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],
[`@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 = (
@@ -88,12 +111,10 @@ These modifiers are semantically mutually exclusive. When multiple are used,
block(
grid(
columns: (auto, 1fr, auto),
output,
repeat([#h(0.25em) . #h(0.25em)]),
entry.pages.join(", "),
output, repeat([#h(0.25em) . #h(0.25em)]), entry.pages.join(", "),
),
)
)
}
},
)
#glossary(theme: my-theme)
+53 -22
View File
@@ -7,20 +7,23 @@
short: "HTML",
long: "Hypertext Markup Language",
description: "A standard language for creating web pages",
group: "Web"),
group: "Web",
),
css: (
short: "CSS",
long: "Cascading Style Sheets",
description: "A language used for describing the presentation of a document",
group: "Web"),
group: "Web",
),
tps: (
short: "TPS",
long: "test procedure specification",
description: "A document on how to run all the test procedures"),
unused: "Unused term, which shouldn't print in the glossary."
description: "A document on how to run all the test procedures",
),
unused: "Unused term, which shouldn't print in the glossary.",
)
#show: init-glossary.with(myGlossary, show-term: (body) => [#emph(body)])
#show: init-glossary.with(myGlossary, show-term: body => [#emph(body)])
#set heading(numbering: "1.1")
#set page(height: auto, width: 6.5in, margin: 1em, numbering: "1")
@@ -81,8 +84,15 @@ Articles are incompatible with the `pl` (plural) modifier.
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)
#import "/src/gloss.typ": __default-format-term, __gls
#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.\""
@@ -93,14 +103,29 @@ These modifiers are semantically mutually exclusive. When multiple are used,
columns: 2,
table.header([*Input*], [*Error*]),
[`@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],
[`@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 = (
@@ -126,14 +151,20 @@ These modifiers are semantically mutually exclusive. When multiple are used,
block(
grid(
columns: (auto, 1fr, auto),
output,
repeat([#h(0.25em) . #h(0.25em)]),
entry.pages.join(", "),
output, repeat([#h(0.25em) . #h(0.25em)]), entry.pages.join(", "),
),
)
)
}
},
)
#glossary(theme: my-theme)
#glossary(title: "Glossary with just empty group", groups: ("",), theme: theme-basic)
#glossary(title: "Glossary with just Web group", groups: ("Web",), theme: theme-compact)
#glossary(
title: "Glossary with just empty group",
groups: ("",),
theme: theme-basic,
)
#glossary(
title: "Glossary with just Web group",
groups: ("Web",),
theme: theme-compact,
)
+8 -19
View File
@@ -54,7 +54,6 @@
("usage", "a"),
("user", "a"),
("utensil", "a"),
// Acronym tests
("ASCII", "an"),
("CFO", "a"),
@@ -80,9 +79,7 @@
("WWW", "a"),
("X.Y.Z.", "an"),
("YAML", "a"),
// Extra tests (courtesy of LLM)
// Common English words starting with vowels
("idea", "an"),
("island", "an"),
@@ -94,7 +91,6 @@
("octopus", "an"),
("atom", "an"),
("item", "an"),
// Words starting with vowel but "yoo" sound (thus 'a')
("universe", "a"),
("unicycle", "a"),
@@ -106,7 +102,6 @@
("useful", "a"),
("utility", "a"),
("utopia", "a"),
// More common vowel words with a/an
("umbrella", "an"), // repeated but reaffirming
("eggplant", "an"),
@@ -118,7 +113,6 @@
("aroma", "an"),
("ear", "an"),
("ocean", "an"),
// Words starting with 'h' but pronounced, so 'a'
("house", "a"), // repeated for emphasis
("hammer", "a"),
@@ -130,14 +124,12 @@
("holiday", "a"),
("harmony", "a"),
("hover", "a"),
// More silent 'h' words (already in set, but variations)
("honorific", "an"),
("honorarium", "an"),
("honesties", "an"),
("hourly", "an"),
("heirloom", "an"),
// Words starting with 'e' + 'u' combo with "yoo" sound
("Europe", "a"), // repeated
("European", "a"), // repeated
@@ -149,7 +141,6 @@
("euthanasia", "a"),
("eugenics", "a"),
("Eurasia", "a"),
// Common consonant-start words
("book", "a"),
("pen", "a"),
@@ -161,7 +152,6 @@
("river", "a"),
("mountain", "a"),
("cloud", "a"),
// More vowel-start words
("ostrich", "an"),
("amphibian", "an"),
@@ -173,7 +163,6 @@
("aisle", "an"),
("oracle", "an"),
("overcoat", "an"),
// More acronym/initialism tests:
("ATM", "an"), // A = 'ay'
("EPA", "an"), // E = 'ee'
@@ -185,7 +174,6 @@
("OSHA", "an"), // O = 'oh'
("RAM", "an"), // R = 'ar'
("SMS", "an"), // repeated, S = 'ess'
// Acronyms starting with consonant sound letters
("GIF", "a"), // G = 'jee', repeated
("CFO", "a"), // C = 'see', repeated (consonant sound 's')
@@ -197,7 +185,6 @@
("UPS", "a"), // U = 'yoo' sound, hence 'a'
("VIP", "a"), // V = 'vee' starts with 'v' consonant sound
("WIPO", "a"), // W = 'double-u' starts with 'd' consonant sound
// More tricky 'u' words with 'yoo' sound for 'a'
("unify", "a"),
("unilateral", "a"),
@@ -209,7 +196,6 @@
("ugly", "an"), // repeated, 'uh-glee'
("upper", "an"), // 'uh-pur' (vowel sound but actually 'uh' sound = 'an'? Actually "upper" starts with a simple vowel sound 'uh', so should it be 'an'? It's commonly "an upper hand".
("update", "an"), // 'up-date' starts with 'uh' vowel sound → 'an update' normally. Actually we say "an update" commonly. Correcting to 'an':
// Words starting with 'u' that actually take "an"
// (because they start with a vowel sound 'uh'):
("under", "an"),
@@ -225,7 +211,6 @@
("universal", "a"),
("umbrella", "an"), // repeated
("upset", "an"), // 'uh-pset' vowel start
// More vowel words to ensure coverage:
("animal", "an"),
("image", "an"),
@@ -240,7 +225,6 @@
("item", "an"), // repeated
("isotope", "an"),
("opera", "an"),
// Words starting with vowels but pronounced with consonant sound 'y' → 'a'
("euphemistic", "a"), // repeated
("euphoric", "a"),
@@ -252,7 +236,6 @@
("eureka", "a"), // repeated
("euphonious", "a"),
("Eurasia", "a"),
// Common consonant-start words:
("milk", "a"),
("road", "a"),
@@ -264,7 +247,6 @@
("knife", "a"), // 'knife' actually starts with /n/ sound because 'k' is silent. "a knife" or "an knife"? It's "a knife" (starts with 'n' consonant sound?), actually 'kn' in 'knife' is silent, so 'knife' is pronounced 'nife' with a consonant sound 'n', so "a knife" is correct.
("glass", "a"),
("window", "a"),
// Final checks with tricky silent letters:
("gnome", "a"), // 'g' silent, pronounced 'nome' with 'n' consonant sound → 'a gnome'
("gnat", "a"), // 'nat' consonant sound → 'a gnat'
@@ -280,7 +262,14 @@
#for (word, article) in tests {
let chosen = __determine_article(word)
if chosen != article {
panic("bad choice! word: " + word + ", article: " + article + ", chosen: " + chosen)
panic(
"bad choice! word: "
+ word
+ ", article: "
+ article
+ ", chosen: "
+ chosen,
)
}
}
+24 -15
View File
@@ -5,20 +5,24 @@
short: "HTML",
long: "Hypertext Markup Language",
description: "A standard language for creating web pages",
group: "Web"),
group: "Web",
),
css: (
short: "CSS",
long: "Cascading Style Sheets",
description: "A language used for describing the presentation of a document",
group: "Web"),
group: "Web",
),
tps: (
short: "TPS",
long: "test procedure specification",
description: "A document on how to run all the test procedures"),
description: "A document on how to run all the test procedures",
),
concise: "Concise syntax!",
shortonly: (
short: "shortonly!!!"),
unused: "Unused term, which shouldn't print in the glossary."
short: "shortonly!!!",
),
unused: "Unused term, which shouldn't print in the glossary.",
)
#set page(width: 6.5in, height: auto, margin: 1em)
@@ -26,9 +30,7 @@
#let test-content = {
table(
columns: 3,
table.header(
[`short`], [`long`], [`both`],
),
table.header([`short`], [`long`], [`both`]),
[@html:short], [@html:long], [@html:both],
[@css:short], [@css:long], [@css:both],
@@ -79,7 +81,7 @@
// ---------------------------------------------------------------------------------
#let format-uppercase-short(mode, short-form, long-form) = {
let short-up = upper(short-form);
let short-up = upper(short-form)
if mode == "short" {
short-up
} else if mode == "long" {
@@ -108,8 +110,8 @@
// ---------------------------------------------------------------------------------
#let format-conditional-separator(mode, short-form, long-form) = {
let separator = if short-form.len() > 4 { " / " } else { ", " };
let has_long = long-form != none and long-form != "";
let separator = if short-form.len() > 4 { " / " } else { ", " }
let has_long = long-form != none and long-form != ""
if mode == "short" {
short-form
@@ -124,7 +126,10 @@
}
}
#init-glossary(myGlossary, format-term: format-conditional-separator)[#test-content]
#init-glossary(
myGlossary,
format-term: format-conditional-separator,
)[#test-content]
// ---------------------------------------------------------------------------------
// 1. Braces and Labels
@@ -145,7 +150,9 @@
// 2. Mode-Based Prefix
// Idea: Add a prefix indicating the mode, and then show the term(s).
#let format-mode-prefix(mode, short-form, long-form) = {
let prefix = if mode == "short" { "S:" } else if mode == "long" { "L:" } else { "B:" };
let prefix = if mode == "short" { "S:" } else if mode == "long" {
"L:"
} else { "B:" }
if mode == "short" {
prefix + short-form
} else if mode == "long" {
@@ -161,7 +168,7 @@
// 3. Conditional Fallback for Missing Long
// Idea: If no long-form is provided, use the short-form in all cases.
#let format-fallback-long(mode, short-form, long-form) = {
let has_long = long-form != none and long-form != "";
let has_long = long-form != none and long-form != ""
if mode == "short" {
short-form
} else if mode == "long" {
@@ -193,7 +200,9 @@
// Idea: In both mode, append the length of each string.
#let format-length-info(mode, short-form, long-form) = {
let s_len = str(short-form.len())
let l_len = if long-form != none and long-form != "" { str(long-form.len()) } else { "0" }
let l_len = if long-form != none and long-form != "" {
str(long-form.len())
} else { "0" }
if mode == "short" {
short-form + " [len:" + s_len + "]"
+2 -2
View File
@@ -19,7 +19,7 @@
This should be a link, too: @WWW
#glossary()
]
],
)
// -----------------------------------------------------------------------------
@@ -41,5 +41,5 @@
This should be a link, too: @ML
#print-glossary(entry-list)
]
],
)
+3 -3
View File
@@ -12,21 +12,21 @@ Reference: @pbr, @zbuffer
]
#t("abbreviations") // "string" : str
#t(("abbreviations")) // ("string") : str
#t("abbreviations") // ("string") : str
#t(("abbreviations",)) // ("string",) : array(str)
#t(("abbreviations", "abbreviations")) // ("string","string",) : array(str)
#pagebreak(weak: true)
#t("") // empty string should be the empty group
#t(("")) // empty string should be the empty group
#t("") // empty string should be the empty group
#t(("",)) // empty string should be the empty group
#t(("", "")) // empty string should be the empty group
#pagebreak(weak: true)
#t(none) // none should be all groups
#t((none)) // none should be all groups
#t(none) // none should be all groups
#pagebreak(weak: true)
+7 -4
View File
@@ -5,17 +5,20 @@
short: "HTML",
long: "Hypertext Markup Language",
description: "A standard language for creating web pages",
group: "Web"),
group: "Web",
),
css: (
short: "CSS",
long: "Cascading Style Sheets",
description: "A language used for describing the presentation of a document",
group: "Web"),
group: "Web",
),
tps: (
short: "TPS",
long: "test procedure specification",
description: "A document on how to run all the test procedures"),
unused: "Unused term, which shouldn't print in the glossary."
description: "A document on how to run all the test procedures",
),
unused: "Unused term, which shouldn't print in the glossary.",
)
#show: init-glossary.with(myGlossary)
+7 -4
View File
@@ -5,17 +5,20 @@
short: "HTML",
long: "Hypertext Markup Language",
description: "A standard language for creating web pages",
group: "Web"),
group: "Web",
),
css: (
short: "CSS",
long: "Cascading Style Sheets",
description: "A language used for describing the presentation of a document",
group: "Web"),
group: "Web",
),
tps: (
short: "TPS",
long: "test procedure specification",
description: "A document on how to run all the test procedures"),
unused: "Unused term, which shouldn't print in the glossary."
description: "A document on how to run all the test procedures",
),
unused: "Unused term, which shouldn't print in the glossary.",
)
#show: init-glossary.with(myGlossary)
+1 -1
View File
@@ -20,7 +20,7 @@
tps: (
short: "TPS",
long: "test procedure specification",
description: "A document on how to run all the test procedures"
description: "A document on how to run all the test procedures",
),
)
+1 -1
View File
@@ -298,7 +298,7 @@
+ ". Expected: "
+ (if expected == none { "none" } else { str(expected) })
+ ", got: "
+ (if result == none { "none" } else { str(result) })
+ (if result == none { "none" } else { str(result) }),
)
}
}
+46 -39
View File
@@ -37,43 +37,37 @@
short: "HTML",
long: "Hypertext Markup Language",
description: "A standard language for creating web pages",
group: "Web"
group: "Web",
),
css: (
short: "CSS",
long: "Cascading Style Sheets",
description: "A language used for describing the presentation of documents",
group: "Web"
group: "Web",
),
// Term with no description
js: (
short: "JS",
long: "JavaScript",
group: "Web"
group: "Web",
),
// Term with only a short field
tcp: (
short: "TCP",
group: "Networking"
group: "Networking",
),
// Term with only a long field
// should panic!
//udp: (
// long: "User Datagram Protocol",
// group: "Networking"
//),
// Term with short and description, but no long
ip: (
short: "IP",
description: "A unique identifier for a host or network interface",
group: "Networking"
group: "Networking",
),
// Term with custom articles and everything defined
api: (
short: "API",
@@ -81,35 +75,30 @@
description: "A set of protocols and tools for building software",
article: "an", // custom article for short form
longarticle: "an", // custom article for long form
group: "Software"
group: "Software",
),
// Term starting with a vowel, no custom article, to test default a/an logic
xml: (
short: "XML",
long: "Extensible Markup Language",
description: "A markup language defining rules for encoding documents",
group: "Data"
group: "Data",
),
// Term starting with a consonant, no custom article
cpu: (
short: "CPU",
long: "Central Processing Unit",
description: "The primary component of a computer that performs calculations",
group: "Hardware"
group: "Hardware",
),
// Term without any fields (just a string), should be handled gracefully
bare: "A bare term with no fields",
// Term with only description
// should panic!
//desconly: (
// description: "Just a description, no short or long",
// group: "Misc"
//),
// Term with article fields but missing short and long (should test fallback)
// should panic!
//articonly: (
@@ -118,51 +107,46 @@
// description: "Has only articles and a description",
// group: "Misc"
//),
// Term with uppercase key
SSL: (
short: "SSL",
long: "Secure Sockets Layer",
description: "A protocol for encrypting information over the internet",
group: "Security"
group: "Security",
),
// Term that will not be referenced
unusedterm: (
short: "UNUSED",
long: "Unused Term",
description: "Should not appear since it's never referenced"
description: "Should not appear since it's never referenced",
),
// Term that tests pluralization with custom fields maybe
doc: (
short: "doc",
long: "document",
description: "A single piece of written matter",
group: "Misc"
group: "Misc",
),
// Terms that might require checking conflicting modifiers
tps: (
short: "TPS",
long: "test procedure specification",
description: "A document describing test procedures",
group: "Testing"
group: "Testing",
),
// Term that might appear multiple times
http: (
short: "HTTP",
long: "Hypertext Transfer Protocol",
description: "A protocol for distributed, collaborative, hypermedia information systems",
group: "Web"
)
group: "Web",
),
)
// Initialize the glossary with a custom show-term callback
#show: init-glossary.with(
exhaustiveGlossary,
show-term: (body) => [#strong(body)]
show-term: body => [#strong(body)],
)
#set heading(numbering: "1.1")
@@ -294,8 +278,15 @@ This document tries all reference forms and modifiers on a large set of terms.
== Conflicting multiple modifiers combined (short, long, both together)
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)
#import "/src/gloss.typ": __default-format-term, __gls
#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
@@ -360,7 +351,7 @@ Assert that a panic occurs.
[`@api:cap` ], [@api:cap], ["capitalize"],
[`@api:pl` ], [@api:pl], ["plural"],
[`@api:long:cap`], [@api:long:cap], ["long + cap"],
[`@api:both:pl` ], [@api:both:pl], ["both + pl"]
[`@api:both:pl` ], [@api:both:pl], ["both + pl"],
)
== Multiple glossaries with themes and group filtering
@@ -377,7 +368,11 @@ Assert that a panic occurs.
== Networking Only
#glossary(title: "Networking Terms", groups: ("Networking",), theme: theme-basic)
#glossary(
title: "Networking Terms",
groups: ("Networking",),
theme: theme-basic,
)
== Misc and Data
@@ -385,17 +380,29 @@ Assert that a panic occurs.
== Just Testing terms
#glossary(title: "Testing Terms", groups: ("Testing",), theme: theme-chicago-index)
#glossary(
title: "Testing Terms",
groups: ("Testing",),
theme: theme-chicago-index,
)
== Hardware group
#glossary(title: "Hardware Terms", groups: ("Hardware",), theme: theme-chicago-index)
#glossary(
title: "Hardware Terms",
groups: ("Hardware",),
theme: theme-chicago-index,
)
== Unreferenced entries
#glossary(title: "Unreferenced (Should Contain Unused?)", groups: ("Security",), theme: theme-chicago-index)
#glossary(
title: "Unreferenced (Should Contain Unused?)",
groups: ("Security",),
theme: theme-chicago-index,
)
== Empty group glossary
#glossary(title: "Empty Group Glossary", groups: (""), theme: theme-basic)
#glossary(title: "Empty Group Glossary", groups: "", theme: theme-basic)
= End of Brutal Test
+5 -2
View File
@@ -25,7 +25,7 @@
#outline(title: "List of Figures", target: figure.where(kind: image))
#block(
height: 7em,
glossary(title: "List of Abbreviations", theme: theme-twocol)
glossary(title: "List of Abbreviations", theme: theme-twocol),
)
#line(length: 100%)
@@ -47,7 +47,10 @@
#figure([], caption: [`@ShortInCaption:short` = "@ShortInCaption:short"])
#figure([], caption: [`@LongInCaption:long` = "@LongInCaption:long"])
#figure([], caption: [`@BothInCaption:both:nospend` = "@BothInCaption:both:nospend"])
#figure(
[],
caption: [`@BothInCaption:both:nospend` = "@BothInCaption:both:nospend"],
)
#figure([], caption: [`@NormalInCaption` = "@NormalInCaption"])
#line(length: 100%)
+4 -4
View File
@@ -2,7 +2,7 @@
#show: init-glossary.with((
one: "this is term one",
two: "term two also"
two: "term two also",
))
#set page(
@@ -19,15 +19,15 @@ shown anyway.
#glossary(
theme: theme-chicago-index,
show-all: true
show-all: true,
)
#glossary(
theme: theme-compact,
show-all: true
show-all: true,
)
#glossary(
theme: theme-table,
show-all: true
show-all: true,
)
+2 -2
View File
@@ -22,11 +22,11 @@
tps: (
short: "TPS",
long: "test procedure specification",
description: "A document on how to run all the test procedures"
description: "A document on how to run all the test procedures",
),
)
#show: init-glossary.with(myGlossary, show-term: (body) => [#emph(body)])
#show: init-glossary.with(myGlossary, show-term: body => [#emph(body)])
#set page(numbering: "1")
+4 -4
View File
@@ -8,7 +8,7 @@
),
iphone: (
short: "iPhone",
description: "Revolutionized phones."
description: "Revolutionized phones.",
),
css: (
short: "CSS",
@@ -21,11 +21,11 @@
tps: (
short: "TPS",
long: "test procedure specification",
description: "A document on how to run all the test procedures"
description: "A document on how to run all the test procedures",
),
)
#show: init-glossary.with(myGlossary, show-term: (body) => [#emph(body)])
#show: init-glossary.with(myGlossary, show-term: body => [#emph(body)])
#set page(
margin: 1cm,
@@ -50,6 +50,6 @@
inset: 1em,
stroke: 1pt + gray,
height: if theme.first() == "theme-twocol" { 1.5in } else { auto },
glossary(title: raw(theme.first()), theme: theme.last(), ignore-case: true)
glossary(title: raw(theme.first()), theme: theme.last(), ignore-case: true),
)
}