From fa440049e130ff8ad741d03ed8ef7aa3132a8cc6 Mon Sep 17 00:00:00 2001 From: Arseny <20096ninja@gmail.com> Date: Mon, 7 Sep 2026 17:06:17 +0300 Subject: [PATCH] =?UTF-8?q?[ADD]=20=D0=BF=D0=BE=D0=BC=D0=B5=D1=82=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BE=D0=BA=20=3D=3D=D1=82?= =?UTF-8?q?=D0=B5=D0=BA=D1=81=D1=82=3D=3D=20=D0=B6=D1=91=D0=BB=D1=82=D1=8B?= =?UTF-8?q?=D0=BC=20=D0=B2=D1=8B=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Рецензент просит показывать в отчёте только исправленные места, чтобы не перечитывать текст целиком. Разметка разбирается на уровне текстовых узлов, поэтому «==» в листингах на C++ остаётся сравнением. Выделяется каждое слово отдельно: одной плашкой на всё выделение строка не разорвётся по его середине, а выделять приходится и целые предложения. Составное слово делится по дефису — плашка неразрывна, и шорткат переноса внутри неё не работает. soul не подошёл: под XeLaTeX его \hl теряет кириллицу. --- README.md | 1 + md2gost/handlers/inline.py | 42 +++++++++++++++++++++++++++++++++++++- tests/test_render.py | 24 ++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c568dda..bfeb047 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ docker build -t md2gost . - Изображения (см. ниже) - Аббревиатуры (раздел "Перечень принятых сокращений") - Ссылки, жирный, курсив +- Пометка правок `==текст==` — жёлтое выделение для рецензента ### Изображения diff --git a/md2gost/handlers/inline.py b/md2gost/handlers/inline.py index d1b5aff..a32988d 100644 --- a/md2gost/handlers/inline.py +++ b/md2gost/handlers/inline.py @@ -81,9 +81,49 @@ def bind_initials(text: str) -> str: return text +# ==текст== — пометка правки для рецензента. Разметки такого рода в +# CommonMark нет, поэтому она разбирается здесь, на уровне текстовых узлов: +# внутрь листингов и инлайн-кода регулярное выражение не попадает, и «==» +# в исходниках на C++ остаётся сравнением. Границы выделения примыкают к +# тексту вплотную — как у прочих парных знаков разметки. +_MARK_RE = re.compile(r"==(?=\S)(.+?)(?<=\S)==") + + +def render_text(text: str) -> str: + return split_compounds(bind_initials(escape_latex(text))) + + +def highlight_word(word: str) -> str: + """Слово в жёлтой плашке, составное — по плашке на каждую часть. + + Плашка неразрывна, поэтому шорткат переноса «"=» внутри неё не работает + и «аппаратно-программный» целиком вылезает за поле. Дефис получает свою + плашку, а точка переноса ставится после неё. + """ + parts = [part for part in word.split("-") if part] + return "\\hlw{-}\\allowbreak ".join(f"\\hlw{{{part}}}" for part in parts) + + +def highlight(text: str) -> str: + """Жёлтая плашка на каждом слове. + + Одной плашкой на всё выделение строка не разорвётся по его середине, + а выделять приходится и целые предложения. + """ + escaped = bind_initials(escape_latex(text)) + return " ".join(highlight_word(word) for word in escaped.split(" ") if word) + + @inline("text") def _text(tok: Token) -> str: - return split_compounds(bind_initials(escape_latex(tok.content))) + parts: list[str] = [] + end = 0 + for match in _MARK_RE.finditer(tok.content): + parts.append(render_text(tok.content[end : match.start()])) + parts.append(highlight(match.group(1))) + end = match.end() + parts.append(render_text(tok.content[end:])) + return "".join(parts) @inline("strong_open") diff --git a/tests/test_render.py b/tests/test_render.py index 52e17b4..e63a1ed 100644 --- a/tests/test_render.py +++ b/tests/test_render.py @@ -37,6 +37,30 @@ def test_unnumbered_heading() -> None: assert "ВВЕДЕНИЕ" in tex +def test_mark_highlights_every_word() -> None: + tex = render("Обычный ==правленый текст== дальше.\n") + assert "\\hlw{правленый} \\hlw{текст}" in tex + assert "Обычный " in tex + assert " дальше." in tex + + +def test_mark_splits_compound_word_into_separate_boxes() -> None: + # Плашка неразрывна: составное слово одним боксом вылезает за поле. + tex = render("==аппаратно-программный комплекс==\n") + assert "\\hlw{аппаратно}\\hlw{-}\\allowbreak \\hlw{программный}" in tex + + +def test_mark_in_heading_reaches_the_contents() -> None: + tex = render("## ==Контейнеризация серверного приложения==\n") + assert "\\subsection{\\hlw{Контейнеризация}" in tex + + +def test_comparison_in_listing_is_not_a_mark() -> None: + tex = body(render("```cpp\nif (a==b && c==d) return;\n```\n")) + assert "\\hlw" not in tex + assert "a==b" in tex + + def test_list_punctuation() -> None: tex = render("- один\n- два\n- три\n") assert " \\item один;" in tex