Initial commit
This commit is contained in:
commit
41d230d524
163 files changed
+11500
No files matched your search
@@ -0,0 +1,247 @@
|
||||
// ==========================================
|
||||
// PRESENTATION: ССЫЛКИ И БИБЛИОГРАФИИ
|
||||
// ==========================================
|
||||
|
||||
#let ensure-label(lbl, scope) = {
|
||||
if type(lbl) != label {
|
||||
panic("Scientia / " + scope + ": ожидался label")
|
||||
}
|
||||
lbl
|
||||
}
|
||||
|
||||
// В тексте чаще всего нужен предложный падеж, поэтому он используется без
|
||||
// второго аргумента. Для явного выбора достаточно одной буквы:
|
||||
// #vref(<figure-label>, "и") или #vref(<figure-label>, "р").
|
||||
// Прежние сокращения и полные названия сохранены как понятные альтернативы.
|
||||
#let case-aliases = (
|
||||
и: "имен",
|
||||
им: "имен",
|
||||
имен: "имен",
|
||||
именительный: "имен",
|
||||
р: "род",
|
||||
род: "род",
|
||||
родительный: "род",
|
||||
д: "дат",
|
||||
дат: "дат",
|
||||
дательный: "дат",
|
||||
в: "вин",
|
||||
вин: "вин",
|
||||
винительный: "вин",
|
||||
т: "тв",
|
||||
тв: "тв",
|
||||
творительный: "тв",
|
||||
п: "предл",
|
||||
пр: "предл",
|
||||
предл: "предл",
|
||||
предложный: "предл",
|
||||
)
|
||||
|
||||
#let ensure-case(value) = {
|
||||
let normalized = if type(value) == str {
|
||||
case-aliases.at(value, default: none)
|
||||
} else {
|
||||
none
|
||||
}
|
||||
if normalized == none {
|
||||
panic(
|
||||
"Scientia / reference: неизвестный падеж " + repr(value)
|
||||
+ "; используйте и, р, д, в, т или п",
|
||||
)
|
||||
}
|
||||
normalized
|
||||
}
|
||||
|
||||
#let resolve-ref-element(lbl) = {
|
||||
let lbl = ensure-label(lbl, "reference")
|
||||
let elems = query(selector(lbl))
|
||||
if elems == () { none } else { elems.first() }
|
||||
}
|
||||
|
||||
#let classify-ref-element(el) = {
|
||||
if el.func() == figure {
|
||||
if el.kind == table { "table" } else { "image" }
|
||||
} else if el.func() == math.equation {
|
||||
"equation"
|
||||
} else if el.func() == heading {
|
||||
if el.supplement == [Приложение] { "attachment" } else { "section" }
|
||||
} else {
|
||||
"other"
|
||||
}
|
||||
}
|
||||
|
||||
#let noun-forms = (
|
||||
image: (
|
||||
singular: (имен: "рисунок", род: "рисунка", дат: "рисунку", вин: "рисунок", тв: "рисунком", предл: "рисунке"),
|
||||
plural: (имен: "рисунки", род: "рисунков", дат: "рисункам", вин: "рисунки", тв: "рисунками", предл: "рисунках"),
|
||||
),
|
||||
table: (
|
||||
singular: (имен: "таблица", род: "таблицы", дат: "таблице", вин: "таблицу", тв: "таблицей", предл: "таблице"),
|
||||
plural: (имен: "таблицы", род: "таблиц", дат: "таблицам", вин: "таблицы", тв: "таблицами", предл: "таблицах"),
|
||||
),
|
||||
equation: (
|
||||
singular: (имен: "формула", род: "формулы", дат: "формуле", вин: "формулу", тв: "формулой", предл: "формуле"),
|
||||
plural: (имен: "формулы", род: "формул", дат: "формулам", вин: "формулы", тв: "формулами", предл: "формулах"),
|
||||
),
|
||||
section: (
|
||||
singular: (имен: "раздел", род: "раздела", дат: "разделу", вин: "раздел", тв: "разделом", предл: "разделе"),
|
||||
plural: (имен: "разделы", род: "разделов", дат: "разделам", вин: "разделы", тв: "разделами", предл: "разделах"),
|
||||
),
|
||||
attachment: (
|
||||
singular: (имен: "приложение", род: "приложения", дат: "приложению", вин: "приложение", тв: "приложением", предл: "приложении"),
|
||||
plural: (имен: "приложения", род: "приложений", дат: "приложениям", вин: "приложения", тв: "приложениями", предл: "приложениях"),
|
||||
),
|
||||
other: (
|
||||
singular: (имен: "элемент", род: "элемента", дат: "элементу", вин: "элемент", тв: "элементом", предл: "элементе"),
|
||||
plural: (имен: "элементы", род: "элементов", дат: "элементам", вин: "элементы", тв: "элементами", предл: "элементах"),
|
||||
),
|
||||
)
|
||||
|
||||
#let noun-for-case(kind, grammatical-case, plural: false, capitalized: false) = {
|
||||
let grammatical-case = ensure-case(grammatical-case)
|
||||
let forms = noun-forms.at(kind, default: noun-forms.other)
|
||||
let number-forms = if plural { forms.plural } else { forms.singular }
|
||||
let noun = number-forms.at(grammatical-case)
|
||||
if capitalized {
|
||||
let characters = noun.clusters()
|
||||
upper(characters.first()) + characters.slice(1).join()
|
||||
} else {
|
||||
noun
|
||||
}
|
||||
}
|
||||
|
||||
#let render-resolved-ref(lbl, grammatical-case, capitalized: false) = {
|
||||
let el = resolve-ref-element(lbl)
|
||||
if el == none {
|
||||
text(fill: red)[Ссылка не найдена]
|
||||
} else {
|
||||
let kind = classify-ref-element(el)
|
||||
let prefix = noun-for-case(kind, grammatical-case, capitalized: capitalized)
|
||||
if kind == "equation" {
|
||||
[#prefix #ref(lbl, supplement: none)]
|
||||
} else {
|
||||
ref(lbl, supplement: prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#let join-reference-items(items, conjunction) = {
|
||||
if items.len() == 1 {
|
||||
items.first()
|
||||
} else if items.len() == 2 {
|
||||
[#items.first() #conjunction #items.last()]
|
||||
} else {
|
||||
for (index, item) in items.enumerate() {
|
||||
if index > 0 {
|
||||
if index == items.len() - 1 { [ #conjunction ] } else { [, ] }
|
||||
}
|
||||
item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#let parse-reference-options(options, scope, allow-conjunction: false) = {
|
||||
let positional = options.pos()
|
||||
let named = options.named()
|
||||
let allowed = if allow-conjunction {
|
||||
("grammatical-case", "capitalized", "conjunction")
|
||||
} else {
|
||||
("grammatical-case", "capitalized")
|
||||
}
|
||||
for key in named.keys() {
|
||||
if key not in allowed {
|
||||
panic("Scientia / " + scope + ": неизвестный параметр " + repr(key))
|
||||
}
|
||||
}
|
||||
if positional.len() > 1 {
|
||||
panic("Scientia / " + scope + ": после label допустим только один позиционный падеж")
|
||||
}
|
||||
if positional.len() == 1 and "grammatical-case" in named {
|
||||
panic("Scientia / " + scope + ": падеж задан одновременно позиционно и по имени")
|
||||
}
|
||||
let grammatical-case = if positional.len() == 1 {
|
||||
positional.first()
|
||||
} else {
|
||||
named.at("grammatical-case", default: "п")
|
||||
}
|
||||
let capitalized = named.at("capitalized", default: true)
|
||||
if type(capitalized) != bool {
|
||||
panic("Scientia / " + scope + ".capitalized: ожидалось bool")
|
||||
}
|
||||
(
|
||||
grammatical-case: ensure-case(grammatical-case),
|
||||
capitalized: capitalized,
|
||||
)
|
||||
}
|
||||
|
||||
#let vref(lbl, ..options) = context {
|
||||
let parsed = parse-reference-options(options, "vref")
|
||||
render-resolved-ref(
|
||||
lbl,
|
||||
parsed.grammatical-case,
|
||||
capitalized: parsed.capitalized,
|
||||
)
|
||||
}
|
||||
|
||||
#let vrefs(labels, ..options) = context {
|
||||
if type(labels) != array or labels.len() == 0 {
|
||||
panic("Scientia / vrefs: нужен непустой array labels")
|
||||
}
|
||||
let parsed = parse-reference-options(options, "vrefs", allow-conjunction: true)
|
||||
let grammatical-case = parsed.grammatical-case
|
||||
let capitalized = parsed.capitalized
|
||||
let conjunction = options.named().at("conjunction", default: [и])
|
||||
for item in labels { let _ = ensure-label(item, "vrefs") }
|
||||
let resolved = labels.map(resolve-ref-element)
|
||||
let groupable = resolved.all(el => el != none) and resolved.len() > 1
|
||||
if groupable {
|
||||
let kind = classify-ref-element(resolved.first())
|
||||
let same-kind = resolved.all(el => classify-ref-element(el) == kind)
|
||||
if same-kind {
|
||||
let prefix = noun-for-case(
|
||||
kind,
|
||||
grammatical-case,
|
||||
plural: true,
|
||||
capitalized: capitalized,
|
||||
)
|
||||
let rendered = labels.map(lbl => ref(lbl, supplement: none))
|
||||
[#prefix #join-reference-items(rendered, conjunction)]
|
||||
} else {
|
||||
let rendered = labels.map(lbl => render-resolved-ref(lbl, grammatical-case, capitalized: capitalized))
|
||||
join-reference-items(rendered, conjunction)
|
||||
}
|
||||
} else {
|
||||
let rendered = labels.map(lbl => render-resolved-ref(lbl, grammatical-case, capitalized: capitalized))
|
||||
join-reference-items(rendered, conjunction)
|
||||
}
|
||||
}
|
||||
|
||||
#let eqref(lbl) = context {
|
||||
let el = resolve-ref-element(lbl)
|
||||
if el == none {
|
||||
text(fill: red)[Ссылка не найдена]
|
||||
} else if classify-ref-element(el) != "equation" {
|
||||
text(fill: red)[Метка не является формулой]
|
||||
} else {
|
||||
ref(lbl, supplement: none)
|
||||
}
|
||||
}
|
||||
|
||||
#let render-bibliographies(sections) = {
|
||||
// Typst 0.15: у каждой bibliography собственный heading. Смещение уровня
|
||||
// отделяет заголовок библиографии от заголовков отчёта и устраняет конфликт
|
||||
// нумерации/outline при нескольких независимых списках.
|
||||
show bibliography: set heading(offset: 1)
|
||||
for (index, section) in sections.enumerate() {
|
||||
if index > 0 and section.at("page-break", default: true) {
|
||||
pagebreak()
|
||||
}
|
||||
bibliography(
|
||||
section.sources,
|
||||
title: section.title,
|
||||
full: section.full,
|
||||
style: section.style,
|
||||
target: section.target,
|
||||
group: section.group,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user