v0.5.4
NewPromt - small fix
This commit is contained in:
@@ -77,6 +77,7 @@ class ListBlock:
|
||||
ordered: bool
|
||||
items: list[str]
|
||||
level: int = 1
|
||||
item_levels: list[int] | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
+22
-7
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user