update
This commit is contained in:
@@ -32,8 +32,46 @@ def _norm_title(s: str) -> str:
|
||||
return s.casefold()
|
||||
|
||||
|
||||
# Bracket-aware separator handling: a ' — ' (or a dash that _norm_dash would normalize) INSIDE
|
||||
# ()[]{} is part of the title, not a title/description separator. Without this a spaced dash within
|
||||
# a math title truncates it: 'Aε-Algorithmus (Güte 1+ε, Laufzeit O(n³)) — …' → 'Aε-Algorithmus (Güte…'.
|
||||
_BRACKETS = {"(": ")", "[": "]", "{": "}"}
|
||||
_CLOSERS = {v: k for k, v in _BRACKETS.items()}
|
||||
|
||||
|
||||
def _unclosed(s: str) -> bool:
|
||||
"""True if s has a dangling opening bracket ()[]{} — the symptom of a mid-bracket cut. Extra
|
||||
closers (more ')' than '(') do NOT count; only an unclosed opener signals a truncated title."""
|
||||
depth = 0
|
||||
for c in s:
|
||||
if c in _BRACKETS:
|
||||
depth += 1
|
||||
elif c in _CLOSERS and depth > 0:
|
||||
depth -= 1
|
||||
return depth > 0
|
||||
|
||||
|
||||
def _split_top(s: str, sep: str) -> str:
|
||||
"""First bracket-depth-0 segment of s split on sep; whole s if sep never occurs at depth 0."""
|
||||
depth = 0
|
||||
for i, c in enumerate(s):
|
||||
if c in _BRACKETS:
|
||||
depth += 1
|
||||
elif c in _CLOSERS and depth > 0:
|
||||
depth -= 1
|
||||
elif depth == 0 and s.startswith(sep, i):
|
||||
return s[:i]
|
||||
return s
|
||||
|
||||
|
||||
def _title(entry: str) -> str:
|
||||
return entry.split(" — ")[0].strip() or entry
|
||||
"""Title = text before the first TOP-LEVEL ' — '. A separator inside ()[]{} does not split
|
||||
(keeps math titles intact); if the extracted title has an unclosed bracket (malformed source),
|
||||
keep the whole entry rather than emit a truncated title."""
|
||||
title = _split_top(entry, " — ")
|
||||
if _unclosed(title):
|
||||
return entry.strip() or entry
|
||||
return title.strip() or entry
|
||||
|
||||
|
||||
def clean_title(s: str) -> str:
|
||||
@@ -74,6 +112,9 @@ def _resolve_title(idx: dict[str, int], t: str) -> int | None:
|
||||
return idx.get(_norm_title(t)) or idx.get(_norm_title(_title(t)))
|
||||
|
||||
|
||||
_DASH_SEP_RE = re.compile(r"\s*[‒–—―]\s+|\s+[‒–—―]\s*")
|
||||
|
||||
|
||||
def _norm_dash(s: str) -> str:
|
||||
"""Dash variants (en/em/figure/bar) with whitespace on AT LEAST ONE side → uniform separator ' — '.
|
||||
Some models (especially non-western ones) use an en-dash "–" instead of the em-dash; without
|
||||
@@ -81,8 +122,12 @@ def _norm_dash(s: str) -> str:
|
||||
space ("Titel —Beschreibung" / "Titel— Beschreibung") also breaks the split and leaks the source
|
||||
filename into the description — so a dash with a space on either side is repaired too. The ASCII
|
||||
hyphen "-" is deliberately NOT in the class (would split "n - 1"/"3-SAT"); requiring ≥1 surrounding
|
||||
space keeps glued compounds like "Backtracking—Verfahren" and number ranges like "12–15" untouched."""
|
||||
return re.sub(r"\s*[‒–—―]\s+|\s+[‒–—―]\s*", " — ", s)
|
||||
space keeps glued compounds like "Backtracking—Verfahren" and number ranges like "12–15" untouched.
|
||||
A qualifying dash INSIDE an open bracket ()[]{} is left untouched — it is part of the title, not a
|
||||
separator (else 'Algorithmus (Güte 1+ε – O(n³)) — …' would split mid-parenthetical)."""
|
||||
def _repl(m):
|
||||
return m.group(0) if _unclosed(s[:m.start()]) else " — "
|
||||
return _DASH_SEP_RE.sub(_repl, s)
|
||||
|
||||
|
||||
def _parse_selection(text: str) -> dict[int, str]:
|
||||
|
||||
Reference in New Issue
Block a user