Glossar: Sprungleiste, Abkürzungen ausschreiben, CLAUDE.md-Regel
Sprungleiste (A B C ... Z) im Glossar — wie im Stichwortverzeichnis:
ein Klick je Buchstabe springt zur '## <Buchstabe>'-Überschrift.
Eingebaut in erzeuge_glossar_04.py als {=html}-Rohblock (nur HTML,
im PDF bedeutungslos).
Abkürzungen beim ersten Mal ausschreiben: 'VaR' → 'VaR (Value at Risk)',
'EVPI' → 'EVPI (Expected Value of Perfect Information)' als Link-Text
bei der ersten Nennung je Datei; danach nur die Abkürzung. 19 Abkürzungen
erfasst (VaR, CVaR, KKT, NLP, MILP, EVPI, LP, QP, ...).
CLAUDE.md: Regel 'Glossar-Links: Fachbegriffe im Fließtext automatisch
verlinkt' dokumentiert — resolve_glossar(), LINK_TEXTE,
ABBREV_ERSTE_NENNUNG, _NICHT_AUTO_LINKEN, _ZU_KURZ_ABK, .glossar-link,
{gl:}-Marke, Sprungleiste, {#gloss:<slug>}-Anchor.
This commit is contained in:
parent
e8abb66d75
commit
0e2c011732
47 changed files with 545 additions and 561 deletions
|
|
@ -5,6 +5,11 @@ Jeder Eintrag nennt am Ende den Abschnitt, in dem der Begriff eingeführt wird
|
|||
dorthin führt in der Online-Fassung direkt zur Erklärung im Zusammenhang. Wer einen Begriff an
|
||||
*allen* Fundstellen sucht, benutzt das Stichwortverzeichnis.
|
||||
|
||||
```{=html}
|
||||
<nav class="register-sprungleiste"><a href="#a">A</a> <a href="#b">B</a> <a href="#c">C</a> <a href="#d">D</a> <a href="#e">E</a> <a href="#f">F</a> <a href="#g">G</a> <a href="#h">H</a> <a href="#i">I</a> <a href="#j">J</a> <a href="#k">K</a> <a href="#l">L</a> <a href="#m">M</a> <a href="#n">N</a> <a href="#o">O</a> <a href="#p">P</a> <a href="#q">Q</a> <a href="#r">R</a> <a href="#s">S</a> <a href="#t">T</a> <a href="#u">U</a> <a href="#v">V</a> <a href="#w">W</a> <a href="#y">Y</a> <a href="#z">Z</a></nav>
|
||||
```
|
||||
|
||||
|
||||
## A
|
||||
|
||||
::: {#gloss:absolutbetrag}
|
||||
|
|
|
|||
|
|
@ -157,4 +157,4 @@ Details zu den Lernpfaden stehen in [`00_Vorwort_und_Lesehilfe.md`](00_Vorwort_u
|
|||
|
||||
---
|
||||
|
||||
*Autor / Herausgeber: Dieter Schlüter · Stand: 10. September 2026 v17.38*
|
||||
*Autor / Herausgeber: Dieter Schlüter · Stand: 10. September 2026 v17.51*
|
||||
|
|
|
|||
|
|
@ -1114,7 +1114,7 @@ _GLOSSAR_GL_RE = re.compile(r'\{gl:([^}]+)\}')
|
|||
def resolve_glossar(text: str, fuer_seite: bool = False) -> str:
|
||||
"""Verlinkt Glossar-Begriffe im Fließtext automatisch (erste Nennung je
|
||||
Begriff im uebergebenen Text) und loest {gl:}-Marken auf."""
|
||||
from glossar_eintraege_04 import LINK_TEXTE
|
||||
from glossar_eintraege_04 import LINK_TEXTE, ABBREV_ERSTE_NENNUNG
|
||||
|
||||
# {gl:Begriff}-Marken zuerst: erzwingen einen Link auch fuer Begriffe,
|
||||
# die nicht auto-linkbar sind. 'Begriff' ist der Anzeigetext im Link;
|
||||
|
|
@ -1165,13 +1165,15 @@ def resolve_glossar(text: str, fuer_seite: bool = False) -> str:
|
|||
last = 0
|
||||
for m in _GLOSSAR_TOKEN_RE.finditer(line):
|
||||
seg = line[last:m.start()]
|
||||
seg, t = _glossar_in_segment(seg, texte, seen, fuer_seite)
|
||||
seg, t = _glossar_in_segment(seg, texte, seen, fuer_seite,
|
||||
ABBREV_ERSTE_NENNUNG)
|
||||
treffer += t
|
||||
new_parts.append(seg)
|
||||
new_parts.append(m.group(0)) # Token unverändert
|
||||
last = m.end()
|
||||
rest = line[last:]
|
||||
rest, t = _glossar_in_segment(rest, texte, seen, fuer_seite)
|
||||
rest, t = _glossar_in_segment(rest, texte, seen, fuer_seite,
|
||||
ABBREV_ERSTE_NENNUNG)
|
||||
treffer += t
|
||||
new_parts.append(rest)
|
||||
lines[i] = ''.join(new_parts)
|
||||
|
|
@ -1188,17 +1190,22 @@ def _glossar_slug_fuer(begriff: str) -> str:
|
|||
|
||||
|
||||
def _glossar_in_segment(segment: str, texte: list[tuple[str, str]],
|
||||
seen: set[str], fuer_seite: bool) -> tuple[str, int]:
|
||||
seen: set[str], fuer_seite: bool,
|
||||
abbrev_erste: dict[str, str] | None = None
|
||||
) -> tuple[str, int]:
|
||||
"""Sucht im Fließtext-Segment nach Glossar-Begriffen und ersetzt das
|
||||
erste Vorkommen je Begriff durch einen Link.
|
||||
|
||||
Bei Abkürzungen (abbrev_erste) wird die ERSTE Nennung ausgeschrieben:
|
||||
'VaR' -> 'VaR (Value at Risk)' als Link-Text. Danach nur noch 'VaR'.
|
||||
Alle Link-Texte werden im Original-Segment gesucht (mit finditer, also
|
||||
alle Vorkommen), dann nach Position sortiert und nicht-ueberlappend
|
||||
ersetzt — laengste zuerst bei gleicher Position. So wird 'CP-SAT' als
|
||||
Ganzes erkannt, bevor 'CP' (als Teilstring darin) ein eigenes Match
|
||||
erzeugt. Wird ein Match wegen Ueberlappung übersprungen, wird das
|
||||
naechste Vorkommen desselben Begriffs genommen (falls nicht ueberlappend).
|
||||
erzeugt.
|
||||
"""
|
||||
if abbrev_erste is None:
|
||||
abbrev_erste = {}
|
||||
matches: list[tuple[int, int, str, str]] = []
|
||||
for link_text, slug in texte:
|
||||
if link_text in seen:
|
||||
|
|
@ -1229,7 +1236,9 @@ def _glossar_in_segment(segment: str, texte: list[tuple[str, str]],
|
|||
result.append(segment[last_end:start])
|
||||
ziel = (f'anhang-glossar.md#gloss:{slug}' if fuer_seite
|
||||
else f'#gloss:{slug}')
|
||||
result.append(f'[{link_text}]({ziel}){{.glossar-link}}')
|
||||
# Erste Nennung einer Abkürzung: ausgeschrieben als Link-Text.
|
||||
anzeige = abbrev_erste.get(link_text, link_text)
|
||||
result.append(f'[{anzeige}]({ziel}){{.glossar-link}}')
|
||||
seen.add(link_text)
|
||||
treffer += 1
|
||||
last_end = end
|
||||
|
|
|
|||
|
|
@ -180,7 +180,16 @@ def baue() -> str:
|
|||
break_long_words=False,
|
||||
break_on_hyphens=False))
|
||||
|
||||
zeilen = [KOPFZEILE, "", absatz(EINLEITUNG), ""]
|
||||
# Sprungleiste A B C ... — wie im Stichwortverzeichnis: ein Klick je
|
||||
# Buchstabe springt zur Ueberschrift '## <Buchstabe>' (Pandoc erzeugt daraus
|
||||
# id="a", id="b", ...). Nur im HTML (roh-Block); im PDF meaningless.
|
||||
buchstaben = sorted({anfangsbuchstabe(n) for n, _, _, _ in EINTRAEGE})
|
||||
sprungleiste = ("```{=html}\n"
|
||||
'<nav class="register-sprungleiste">'
|
||||
+ " ".join(f'<a href="#{b.lower()}">{b}</a>' for b in buchstaben)
|
||||
+ "</nav>\n```\n")
|
||||
|
||||
zeilen = [KOPFZEILE, "", absatz(EINLEITUNG), "", sprungleiste, ""]
|
||||
letzter = None
|
||||
for name, idx, text, ziel in sorted(EINTRAEGE,
|
||||
key=lambda e: sortierschluessel(e[0])):
|
||||
|
|
|
|||
|
|
@ -1535,6 +1535,31 @@ _NICHT_AUTO_LINKEN = frozenset({
|
|||
_ZU_KURZ_ABK = frozenset({"OR"})
|
||||
|
||||
|
||||
# ABBREV_ERSTE_NENNUNG: Abkürzung -> Anzeige-Text für die erste Nennung.
|
||||
# 'VaR' -> 'VaR (Value at Risk)', 'EVPI' -> 'EVPI (Expected Value ...)'.
|
||||
# Wird beim ersten Vorkommen der Abkürzung im Fließtext als Link-Text
|
||||
# verwendet; danach nur noch die Abkürzung. Siehe resolve_glossar().
|
||||
ABBREV_ERSTE_NENNUNG: dict[str, str] = {}
|
||||
|
||||
|
||||
def _erste_nennung(name: str, abk: str) -> str:
|
||||
"""Baut die erste-Nennung-Anzeige aus dem Glossar-Namen.
|
||||
|
||||
'Value at Risk (VaR)' + 'VaR' -> 'VaR (Value at Risk)'
|
||||
'EVPI (Expected Value ...)' + 'EVPI' -> 'EVPI (Expected Value ...)' (=name)
|
||||
"""
|
||||
m = _re.search(r"\(([^)]+)\)\s*$", name)
|
||||
if not m:
|
||||
return name
|
||||
in_paren = m.group(1).strip()
|
||||
before = name[: m.start()].strip()
|
||||
# Fall 1: Abkürzung in der Klammer -> 'Abk (Voller Name)'.
|
||||
if in_paren == abk:
|
||||
return f"{abk} ({before})"
|
||||
# Fall 2: Abkürzung vor der Klammer -> Name ist schon 'Abk (Voller Name)'.
|
||||
return name
|
||||
|
||||
|
||||
LINK_TEXTE: dict[str, str] = {}
|
||||
for _name, _idx, _text, _ziel in EINTRAEGE:
|
||||
if _name in _NICHT_AUTO_LINKEN:
|
||||
|
|
@ -1546,3 +1571,5 @@ for _name, _idx, _text, _ziel in EINTRAEGE:
|
|||
_abk = _abkuerzung_aus_name(_name)
|
||||
if _abk and _abk not in _ZU_KURZ_ABK:
|
||||
LINK_TEXTE[_abk] = _slug
|
||||
# Erste Nennung: ausgeschrieben ('VaR (Value at Risk)').
|
||||
ABBREV_ERSTE_NENNUNG[_abk] = _erste_nennung(_name, _abk)
|
||||
|
|
|
|||
Loading…
Reference in a new issue