v0.5.4
Example document / build (push) Has been cancelled
Python application / build (push) Has been cancelled

NewPromt
- small fix
This commit is contained in:
Igor20264
2026-09-19 10:53:19 +03:00
parent f7136bd8bd
commit 5fb5010571
24 changed files with 4336 additions and 22 deletions
+1
View File
@@ -77,6 +77,7 @@ class ListBlock:
ordered: bool
items: list[str]
level: int = 1
item_levels: list[int] | None = None
@dataclass
+22 -7
View File
@@ -26,7 +26,8 @@ APPENDIX_RE = re.compile(
)
NUMBERED_HEADING_RE = re.compile(r"^(\d+(?:\.\d+)*)\.?\s+(.+)$")
PRACTICE_RE = re.compile(r"^Практическая\s+работа\b", re.IGNORECASE)
LIST_MARKER_RE = re.compile(r"^(?:[–—\-•▪]|(\d+)\.)\s*\t?\s*(.*)$")
LIST_MARKER_RE = re.compile(r"^(?:[–—\-•▪]|(\d+(?:\.\d+)*)\.)\s*\t?\s*(.*)$")
ORDERED_MARKER_RE = re.compile(r"^(\d+(?:\.\d+)*)\.\s*\t?\s*(.+)$")
BIBLIO_LINE_RE = re.compile(r"^\[(\d+(?:\.\d+)?)\]\s*[:.]?\s*(.+)$")
BODY_START_TITLES = SPECIAL_UPPER | {"СОДЕРЖАНИЕ"}
@@ -136,23 +137,37 @@ def classify_heading_text(text: str, style_level: int | None) -> tuple[int, str,
return level, raw, True
def look_like_list_item(text: str) -> tuple[bool, bool, str] | None:
def parse_list_item(text: str) -> tuple[bool, int, str] | None:
"""
Detect md2gost-style list lines (–\\t / 1.\\t) or plain markers.
Returns (ordered, marker_ok, body) or None.
Detect md2gost-style list lines (–\\t / 1.\\t / 1.1.\\t).
Returns (ordered, level, body) or None.
"""
t = (text or "").strip()
if not t:
return None
if t[0] in ("–", "—", "-", "•", "▪") and (len(t) == 1 or t[1] in "\t "):
body = t[1:].lstrip("\t ").strip()
return False, True, body
m = re.match(r"^(\d+)\.\s*\t?\s*(.+)$", t)
return False, 1, body
m = ORDERED_MARKER_RE.match(t)
if m:
return True, True, m.group(2).strip()
number = m.group(1)
level = number.count(".") + 1
return True, level, m.group(2).strip()
return None
def look_like_list_item(text: str) -> tuple[bool, bool, str] | None:
"""
Detect md2gost-style list lines (–\\t / 1.\\t) or plain markers.
Returns (ordered, marker_ok, body) or None.
"""
parsed = parse_list_item(text)
if parsed is None:
return None
ordered, _level, body = parsed
return ordered, True, body
def parse_biblio_line(text: str) -> tuple[str, str] | None:
m = BIBLIO_LINE_RE.match((text or "").strip())
if not m:
+11 -3
View File
@@ -216,12 +216,20 @@ def emit_markdown(blocks: list[Block], *, emit_page_breaks: bool = False) -> str
continue
if isinstance(b, ListBlock):
for idx, item in enumerate(b.items, start=1):
levels = b.item_levels or []
counters = [0] * 10
for idx, item in enumerate(b.items):
level = levels[idx] if idx < len(levels) else 1
level = max(1, min(level, 9))
body = escape_md_inline(item)
indent = " " * (level - 1)
if b.ordered:
chunks.append(f"{idx}. {body}")
counters[level - 1] += 1
for deeper in range(level, 10):
counters[deeper] = 0
chunks.append(f"{indent}{counters[level - 1]}. {body}")
else:
chunks.append(f"- {body}")
chunks.append(f"{indent}- {body}")
chunks.append("")
i += 1
continue
+18 -11
View File
@@ -36,8 +36,8 @@ from .classify import (
is_code_style,
is_special_title,
is_toc_style,
look_like_list_item,
parse_biblio_line,
parse_list_item,
paragraph_has_numpr,
style_name,
)
@@ -182,6 +182,7 @@ class DocxWalker:
self._code_caption: CaptionBlock | None = None
self._list_buf: list[str] | None = None
self._list_ordered: bool | None = None
self._list_levels: list[int] | None = None
def walk(self) -> list[Block]:
blocks: list[Block] = []
@@ -225,10 +226,12 @@ class DocxWalker:
ListBlock(
ordered=bool(self._list_ordered),
items=list(self._list_buf),
item_levels=list(self._list_levels or []),
)
)
self._list_buf = None
self._list_ordered = None
self._list_levels = None
def _handle_paragraph(self, paragraph: DocxParagraph) -> list[Block]:
out: list[Block] = []
@@ -408,33 +411,37 @@ class DocxWalker:
return None
self._flush_code(out)
# python-docx does not expose the rendered number, so infer from the text
m = re.match(r"^\d+\.\s*(.*)$", para.text)
if m:
ordered, body = True, m.group(1)
parsed = parse_list_item(para.text)
if parsed is not None:
ordered, level, body = parsed
else:
ordered, body = False, re.sub(r"^[–—\-•▪]\s*", "", para.text)
self._append_list_item(ordered, body, out)
ordered, level, body = False, 1, re.sub(r"^[–—\-•▪]\s*", "", para.text)
self._append_list_item(ordered, body, out, level)
return []
def _as_marker_list_item(self, para: _Para, out: list[Block]) -> list[Block] | None:
list_hit = look_like_list_item(para.text)
parsed = parse_list_item(para.text)
indented = para.paragraph.paragraph_format.left_indent or "\t" in (
para.paragraph.text or ""
)
if not (list_hit and indented):
if not (parsed and indented):
return None
ordered, _ok, body = list_hit
ordered, level, body = parsed
self._flush_code(out)
self._append_list_item(ordered, body, out)
self._append_list_item(ordered, body, out, level)
return []
def _append_list_item(self, ordered: bool, body: str, out: list[Block]) -> None:
def _append_list_item(
self, ordered: bool, body: str, out: list[Block], level: int = 1
) -> None:
if self._list_buf is not None and self._list_ordered == ordered:
self._list_buf.append(body)
self._list_levels.append(level)
return
self._flush_list(out)
self._list_buf = [body]
self._list_ordered = ordered
self._list_levels = [level]
def _as_plain_paragraph(self, para: _Para, out: list[Block]) -> list[Block]:
self._flush_code(out)