v0.5.3
- Мелкие исправления увеличивающие работу конвертера
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
"""Оформление листинга и флаг подсветки синтаксиса.
|
||||
|
||||
Методичка МИРЭА, табл. 4.1 (`_method_extract.txt`, строки 716-737), блок
|
||||
«Содержание листинга»: шрифт Courier New, начертание Обычный, размер 10 пт,
|
||||
цвет текста Авто (чёрный), выравнивание по левому краю, отступы слева/справа
|
||||
0 см, первая строка 0 см, интервал перед и после 0 мм, междустрочный
|
||||
одинарный.
|
||||
|
||||
Отсюда два независимых требования:
|
||||
|
||||
* с выключенной подсветкой (дефолт) содержимое листинга не должно нести
|
||||
никакого прямого форматирования на уровне run'а;
|
||||
* если пользователь всё же включил `--syntax-highlighting`, флаг обязан
|
||||
работать честно — курсив из стиля Pygments должен доезжать до XML, а checker
|
||||
обязан предупредить об отклонении от табл. 4.1.
|
||||
"""
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from contextlib import contextmanager
|
||||
|
||||
import docx
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
|
||||
from docx.oxml.ns import qn
|
||||
from docx.shared import Pt
|
||||
|
||||
from md2gost import package_dir
|
||||
from md2gost.checker import check_markdown
|
||||
from md2gost.renderable.caption import CaptionInfo
|
||||
from md2gost.renderable.listing import Listing
|
||||
from md2gost.styles import apply_mirea_styles
|
||||
|
||||
|
||||
CODE = '# комментарий\nprint("hello")\n'
|
||||
|
||||
MD_WITH_LISTING = """# *СОДЕРЖАНИЕ
|
||||
[TOC]
|
||||
|
||||
# Раздел
|
||||
|
||||
%code1 Пример кода
|
||||
|
||||
```python
|
||||
print(1)
|
||||
```
|
||||
|
||||
См. @Листинг:code1.
|
||||
"""
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _syntax_highlighting(enabled: bool):
|
||||
previous = os.environ.get("SYNTAX_HIGHLIGHTING")
|
||||
if enabled:
|
||||
os.environ["SYNTAX_HIGHLIGHTING"] = "1"
|
||||
else:
|
||||
os.environ.pop("SYNTAX_HIGHLIGHTING", None)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if previous is None:
|
||||
os.environ.pop("SYNTAX_HIGHLIGHTING", None)
|
||||
else:
|
||||
os.environ["SYNTAX_HIGHLIGHTING"] = previous
|
||||
|
||||
|
||||
class _ListingTestCase(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self._document = docx.Document(os.path.join(package_dir(), "Template.docx"))
|
||||
self._document._body.clear_content()
|
||||
apply_mirea_styles(self._document)
|
||||
|
||||
def _listing(self, highlighting: bool) -> Listing:
|
||||
listing = Listing(self._document._body, "python", CaptionInfo("code1", "Пример"))
|
||||
with _syntax_highlighting(highlighting):
|
||||
listing.set_text(CODE)
|
||||
return listing
|
||||
|
||||
@staticmethod
|
||||
def _runs(listing: Listing):
|
||||
for paragraph in listing.paragraphs:
|
||||
for run in paragraph._docx_paragraph.runs:
|
||||
yield run
|
||||
|
||||
|
||||
class TestListingWithoutHighlighting(_ListingTestCase):
|
||||
"""Табл. 4.1: содержимое листинга — Обычный, чёрный, Courier New 10 пт."""
|
||||
|
||||
def test_runs_carry_no_direct_formatting(self):
|
||||
listing = self._listing(highlighting=False)
|
||||
|
||||
runs = list(self._runs(listing))
|
||||
self.assertTrue(runs)
|
||||
for run in runs:
|
||||
rPr = run._element.find(qn("w:rPr"))
|
||||
if rPr is None:
|
||||
continue
|
||||
self.assertIsNone(rPr.find(qn("w:b")), run.text)
|
||||
self.assertIsNone(rPr.find(qn("w:i")), run.text)
|
||||
self.assertIsNone(rPr.find(qn("w:color")), run.text)
|
||||
|
||||
def test_code_style_matches_table_4_1(self):
|
||||
style = self._document.styles["Code"]
|
||||
|
||||
self.assertEqual("Courier New", style.font.name)
|
||||
self.assertEqual(Pt(10), style.font.size)
|
||||
self.assertFalse(style.font.bold)
|
||||
self.assertFalse(style.font.italic)
|
||||
|
||||
pf = style.paragraph_format
|
||||
self.assertEqual(WD_ALIGN_PARAGRAPH.LEFT, pf.alignment)
|
||||
self.assertAlmostEqual(0, pf.first_line_indent.cm, places=3)
|
||||
self.assertAlmostEqual(0, pf.left_indent.cm, places=3)
|
||||
self.assertAlmostEqual(0, pf.space_before.mm, places=3)
|
||||
self.assertAlmostEqual(0, pf.space_after.mm, places=3)
|
||||
self.assertEqual(WD_LINE_SPACING.SINGLE, pf.line_spacing_rule)
|
||||
|
||||
|
||||
class TestListingWithHighlighting(_ListingTestCase):
|
||||
"""Включённый флаг обязан реально давать курсив там, где его задаёт Pygments."""
|
||||
|
||||
def test_italic_token_reaches_xml(self):
|
||||
listing = self._listing(highlighting=True)
|
||||
|
||||
italic_runs = [run for run in self._runs(listing) if run.italic]
|
||||
self.assertTrue(
|
||||
italic_runs,
|
||||
"ни один run не получил курсив, хотя стиль Pygments задаёт italic "
|
||||
"для Token.Comment",
|
||||
)
|
||||
self.assertTrue(
|
||||
any("комментарий" in run.text for run in italic_runs),
|
||||
[run.text for run in italic_runs],
|
||||
)
|
||||
|
||||
|
||||
class TestSyntaxHighlightingChecker(unittest.TestCase):
|
||||
"""Подсветка противоречит табл. 4.1 — checker обязан об этом сказать."""
|
||||
|
||||
def test_no_issue_when_flag_is_off(self):
|
||||
issues = check_markdown(MD_WITH_LISTING, "coursework")
|
||||
|
||||
self.assertEqual(
|
||||
[], [i for i in issues if i.id == "listing.syntax_highlighting"]
|
||||
)
|
||||
|
||||
def test_warning_when_flag_is_on(self):
|
||||
issues = check_markdown(
|
||||
MD_WITH_LISTING, "coursework", syntax_highlighting=True
|
||||
)
|
||||
|
||||
found = [i for i in issues if i.id == "listing.syntax_highlighting"]
|
||||
self.assertEqual(1, len(found))
|
||||
self.assertEqual("warning", found[0].severity)
|
||||
|
||||
def test_error_under_strict(self):
|
||||
issues = check_markdown(
|
||||
MD_WITH_LISTING, "coursework", syntax_highlighting=True, strict=True
|
||||
)
|
||||
|
||||
found = [i for i in issues if i.id == "listing.syntax_highlighting"]
|
||||
self.assertEqual(1, len(found))
|
||||
self.assertEqual("error", found[0].severity)
|
||||
Reference in New Issue
Block a user