- Add\Rework UI - Add Split Table and Listing - Add Support Customazeble schems
This commit is contained in:
+105
-14
@@ -258,12 +258,36 @@ def check_bibliography(text: str, profile: DocProfile) -> list[Issue]:
|
||||
|
||||
n = len(entries)
|
||||
if profile.sectional_biblio:
|
||||
# Count per ## section roughly
|
||||
if n < profile.min_sources:
|
||||
# Count per ## section inside bibliography
|
||||
sections: list[tuple[str, int]] = []
|
||||
current_title: str | None = None
|
||||
current_count = 0
|
||||
for line in block.splitlines():
|
||||
hm = re.match(r"^#{2,6}\s+(\*?)(.+)$", line)
|
||||
if hm:
|
||||
if current_title is not None:
|
||||
sections.append((current_title, current_count))
|
||||
current_title = hm.group(2).strip()
|
||||
current_count = 0
|
||||
continue
|
||||
if BIBLIO_LINE_RE.match(line.strip()):
|
||||
current_count += 1
|
||||
if current_title is not None:
|
||||
sections.append((current_title, current_count))
|
||||
|
||||
if sections:
|
||||
for title, cnt in sections:
|
||||
if cnt < profile.min_sources:
|
||||
issues.append(Issue(
|
||||
"biblio.count", "warning",
|
||||
f"ВКР: в разделе списка «{title}» желательно ≥{profile.min_sources} "
|
||||
f"источников (сейчас {cnt})",
|
||||
))
|
||||
elif n < profile.min_sources:
|
||||
issues.append(Issue(
|
||||
"biblio.count", "warning",
|
||||
f"ВКР: в каждом разделе списка желательно ≥{profile.min_sources} источников "
|
||||
f"(сейчас всего {n})",
|
||||
f"ВКР: список должен делиться на разделы; в каждом ≥{profile.min_sources} "
|
||||
f"источников (сейчас всего {n}, разделов нет)",
|
||||
))
|
||||
else:
|
||||
if n < profile.min_sources:
|
||||
@@ -288,13 +312,10 @@ def check_bibliography(text: str, profile: DocProfile) -> list[Issue]:
|
||||
))
|
||||
|
||||
# Citation order vs first appearance
|
||||
# Exclude intro/conclusion from citation scan for order
|
||||
body_for_cites = text
|
||||
cites = find_citations(body_for_cites)
|
||||
# Filter cites that appear only in intro/conclusion — still listed
|
||||
keys = [e[0] for e in entries]
|
||||
if cites and keys:
|
||||
# First N unique cites should match order of keys for simple lists
|
||||
if not profile.sectional_biblio:
|
||||
expected = cites[:len(keys)]
|
||||
if keys != expected and set(keys) == set(expected):
|
||||
@@ -313,16 +334,15 @@ def check_bibliography(text: str, profile: DocProfile) -> list[Issue]:
|
||||
|
||||
def check_object_refs(text: str) -> list[Issue]:
|
||||
issues = []
|
||||
# Captions / images
|
||||
labels = set(re.findall(r"^%(\w+)", text, re.M))
|
||||
# image titles with %id
|
||||
labels.update(re.findall(r'!\[[^\]]*\]\([^)]*%(\w+)', text))
|
||||
refs = set(re.findall(r"@[\wА-Яа-я]+:(\w+)", text))
|
||||
for lab in labels:
|
||||
# At least one @?:lab or word reference — soft check
|
||||
if lab not in refs and f"@{lab}" not in text:
|
||||
# only warn if label looks intentional
|
||||
pass
|
||||
if lab not in refs:
|
||||
issues.append(Issue(
|
||||
"ref.unused", "warning",
|
||||
f"Метка «{lab}» объявлена, но в тексте нет ссылки @…:{lab}",
|
||||
))
|
||||
for ref in refs:
|
||||
if ref not in labels:
|
||||
issues.append(Issue(
|
||||
@@ -342,6 +362,66 @@ def check_appendices(text: str) -> list[Issue]:
|
||||
f"Буква «{letter}» не используется для обозначения приложений",
|
||||
_line_of(text, m.start()),
|
||||
))
|
||||
|
||||
# Multiple lettered appendices should have a list after # *ПРИЛОЖЕНИЯ
|
||||
apps = list(re.finditer(
|
||||
r"^#{1,6}\s+\*?Приложение\s+[А-ЯA-ZЁ]\b",
|
||||
text, re.M | re.I,
|
||||
))
|
||||
# Filter out the section header «ПРИЛОЖЕНИЯ» / bare «ПРИЛОЖЕНИЕ»
|
||||
lettered = []
|
||||
for m in apps:
|
||||
line = m.group(0)
|
||||
if re.search(r"(?i)приложение\s+[А-ЯA-ZЁ]", line):
|
||||
lettered.append(m)
|
||||
if len(lettered) >= 2:
|
||||
sec = re.search(
|
||||
r"^#\s*\*?\s*ПРИЛОЖЕНИЯ?\s*$",
|
||||
text, re.M | re.I,
|
||||
)
|
||||
if sec:
|
||||
between = text[sec.end(): lettered[0].start()]
|
||||
# Expect a plain-text list (not only blank / headings)
|
||||
has_list = bool(re.search(r"(?im)^\s*[-–—*]|\bприложение\s+[А-ЯA-ZЁ]\b", between))
|
||||
has_prose = bool(re.search(r"[А-Яа-яA-Za-z]{3,}", between))
|
||||
if not (has_list or has_prose):
|
||||
issues.append(Issue(
|
||||
"appendix.toc", "warning",
|
||||
"При нескольких приложениях после «ПРИЛОЖЕНИЯ» нужен перечень "
|
||||
"(основной текст) с номерами и названиями",
|
||||
_line_of(text, sec.start()),
|
||||
))
|
||||
return issues
|
||||
|
||||
|
||||
def check_continuation_hints(
|
||||
text: str,
|
||||
*,
|
||||
table_continuation: str = "off",
|
||||
listing_continuation: str = "off",
|
||||
) -> list[Issue]:
|
||||
"""Warn that Word cannot auto-insert «Продолжение…» when mode is off/soft."""
|
||||
issues: list[Issue] = []
|
||||
soft = {"off", "soft"}
|
||||
if table_continuation in soft and _TABLE_BLOCK_RE.search(text):
|
||||
issues.append(Issue(
|
||||
"table.continuation", "warning",
|
||||
"Word сам не вставит «Продолжение Таблицы N» при переносе. "
|
||||
"Варианты: --table-continuation word (точный разрыв через Word COM) "
|
||||
"или caption (оценка высоты ≠ вёрстка Word, возможны артефакты).",
|
||||
))
|
||||
# Long fenced code blocks (likely listings)
|
||||
long_listing = False
|
||||
for m in re.finditer(r"^```[^\n]*\n([\s\S]*?)^```", text, re.M):
|
||||
if m.group(1).count("\n") >= 40:
|
||||
long_listing = True
|
||||
break
|
||||
if listing_continuation in soft and long_listing:
|
||||
issues.append(Issue(
|
||||
"listing.continuation", "warning",
|
||||
"Длинный листинг: Word сам не вставит «Продолжение Листинга N». "
|
||||
"Варианты: --listing-continuation word или caption.",
|
||||
))
|
||||
return issues
|
||||
|
||||
|
||||
@@ -404,7 +484,13 @@ def check_table_merge(text: str) -> list[Issue]:
|
||||
return issues
|
||||
|
||||
|
||||
def check_markdown(text: str, doc_type: str = "coursework") -> list[Issue]:
|
||||
def check_markdown(
|
||||
text: str,
|
||||
doc_type: str = "coursework",
|
||||
*,
|
||||
table_continuation: str = "off",
|
||||
listing_continuation: str = "off",
|
||||
) -> list[Issue]:
|
||||
profile = get_profile(doc_type)
|
||||
issues: list[Issue] = []
|
||||
issues.extend(check_structure(text, profile))
|
||||
@@ -417,6 +503,11 @@ def check_markdown(text: str, doc_type: str = "coursework") -> list[Issue]:
|
||||
issues.extend(check_object_refs(text))
|
||||
issues.extend(check_appendices(text))
|
||||
issues.extend(check_table_merge(text))
|
||||
issues.extend(check_continuation_hints(
|
||||
text,
|
||||
table_continuation=table_continuation,
|
||||
listing_continuation=listing_continuation,
|
||||
))
|
||||
return issues
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user