BigUpdate
Python application / build (push) Has been cancelled

This commit is contained in:
Igor20264
2026-09-03 10:44:08 +03:00
parent d2da20fdb2
commit 516abe7b83
177 changed files with 40178 additions and 2 deletions
+36
View File
@@ -0,0 +1,36 @@
from marko import Markdown
from marko.ext.gfm import GFM
from marko.helpers import MarkoExtension
from marko import inline, block
from marko.ext.gfm import elements
from marko.inline import *
from marko.inline import InlineElement
from marko.block import *
from marko.block import BlockElement
from marko.ext.gfm.elements import *
from .caption import Caption
from .equation import Equation
from .heading import Heading
from .reference import Reference
from .table import Table
from .toc import TOC
from .inline_formula import InlineEquation
from .image import Image
Extension = MarkoExtension(
elements=[
Equation,
Reference,
Caption,
Table,
TOC,
Heading,
InlineEquation,
Image
]
)
markdown = Markdown(extensions=[GFM, Extension])
+33
View File
@@ -0,0 +1,33 @@
from marko.block import BlockElement
from marko.source import Source
from re import Match, compile as re_compile
_LISTING_FLAG_RE = re_compile(r"(?i)(?:^|\s)\+?listing\b")
class Caption(BlockElement):
"""Represents caption element
Syntax: %label Caption text [+listing]
"""
pattern = r"\%(\w+)( (.+))?"
def __init__(self, match: Match[str]):
self.unique_name = match.group(1)
raw = (match.group(3) or "").strip()
self.with_listing = bool(_LISTING_FLAG_RE.search(raw))
if self.with_listing:
raw = _LISTING_FLAG_RE.sub(" ", raw).strip()
self.text = raw or None
@classmethod
def match(cls, source: Source) -> Match[str] | None:
return source.expect_re(cls.pattern)
@classmethod
def parse(cls, source: Source) -> Match[str] | None:
m = source.match
source.consume()
return m
+26
View File
@@ -0,0 +1,26 @@
import re
from marko.block import BlockElement
from marko.source import Source
from re import Match
class Equation(BlockElement):
"""Represents formula element
Syntax: $$ 2 + 2 = 4 $$"""
pattern = re.compile(r"\$\$([\S\s]*?)\$\$", re.M)
def __init__(self, match: Match[str]):
self.latex_equation = match.group(1).strip()
@classmethod
def match(cls, source: Source) -> Match[str] | None:
return source.expect_re(cls.pattern)
@classmethod
def parse(cls, source: Source) -> Match[str] | None:
m = source.match
source.consume()
return m
+36
View File
@@ -0,0 +1,36 @@
import re
from re import Match
from marko.block import BlockElement
from marko.source import Source
class Heading(BlockElement):
"""Heading element: (### Hello\n)
Asterisk before text means that headings is unnumbered"""
priority = 6
pattern = re.compile(
r" {0,3}(#{1,6})((?=\s)[^\n]*?|[^\n\S]*)(?:(?<=\s)(?<!\\)#+)?[^\n\S]*$\n?",
flags=re.M,
)
override = True
def __init__(self, match: Match[str]) -> None:
self.level = len(match.group(1))
inline_body = match.group(2).strip()
self.numbered = not (inline_body[0] == "*")
if not self.numbered:
inline_body = inline_body[1:]
self.inline_body = inline_body
@classmethod
def match(cls, source: Source) -> Match[str] | None:
return source.expect_re(cls.pattern)
@classmethod
def parse(cls, source: Source) -> Match[str] | None:
m = source.match
source.consume()
return m
+15
View File
@@ -0,0 +1,15 @@
import re
from marko.inline import Image as Image_
class Image(Image_):
override = True
def __init__(self, match):
super().__init__(match)
self.unique_name = None
if self.title and (m := re.match(r"\%(\w+)( (.+))?", self.title)):
self.unique_name = m.group(1)
self.title = (m.group(3) or "").strip() or None
@@ -0,0 +1,14 @@
from marko.inline import InlineElement
from re import Match
class InlineEquation(InlineElement):
"""Represents inline formula element
Syntax: \\( y = x \\)"""
pattern = r"\$(.*?)\$"
priority = 6
def __init__(self, match: Match[str]):
self.latex_equation = match.group(1)
+18
View File
@@ -0,0 +1,18 @@
from marko.inline import InlineElement
from re import Match
class Reference(InlineElement):
"""Represents Reference element
Syntax: @Type:label
Label is only word characters so trailing punctuation in
«@Рисунок:id,» or «(@Рисунок:id)» is not swallowed.
"""
pattern = r"@(\w+):(\w+)"
def __init__(self, match: Match[str]):
self.type = match.group(1)
self.name = match.group(2)
+157
View File
@@ -0,0 +1,157 @@
import re
from marko import block
from marko.ext.gfm.elements import TableCell as GfmTableCell
MERGE_V_MARKERS = {"^", "^^"}
MERGE_H_MARKERS = {">", ">>"}
class TableCell(GfmTableCell):
"""GFM table cell with rowspan/colspan continue markers (^ / >)."""
def __init__(self, text: str, position: int | None = None) -> None:
stripped = text.strip()
self.merge_v = "none"
self.merge_h = "none"
if stripped in MERGE_V_MARKERS:
self.merge_v = "continue"
text = " "
elif stripped in MERGE_H_MARKERS:
self.merge_h = "continue"
text = " "
super().__init__(text, position)
class TableRow(block.BlockElement):
"""A table row element."""
splitter = re.compile(r"\s*(?<!\\)\|\s*")
delimiter = re.compile(r":?-+:?")
virtual = True
_cells = None
_is_delimiter = False
def __init__(self, cells):
self.children = cells
@classmethod
def match(cls, source):
line = source.next_line()
if not line or not re.match(r" {,3}\S", line):
return False
parts = cls.splitter.split(line.strip())
if parts and not parts[0]:
parts.pop(0)
if parts and not parts[-1]:
parts.pop()
if len(parts) < 1:
return False
cls._cells = parts
cls._is_delimiter = all(cls.delimiter.match(cell) for cell in parts)
return True
@classmethod
def parse(cls, source):
source.consume()
parent = source.state
cells = cls._cells[:]
if len(cells) < parent._num_of_cols:
cells.extend("" for _ in range(parent._num_of_cols - len(cells)))
elif len(cells) > parent._num_of_cols:
cells = cells[: parent._num_of_cols]
cells = [TableCell(cell) for cell in cells]
if parent.children:
for head, cell in zip(parent.children[0].children, cells):
cell.align = head.align
return cells
class Table(block.BlockElement):
"""A table element."""
_num_of_cols = None
_prefix = ""
override = True
@classmethod
def match(cls, source):
source.anchor()
if TableRow.match(source) and not TableRow._is_delimiter:
if not TableRow.splitter.search(source.next_line()):
return False
source.pos = source.match.end()
num_of_cols = len(TableRow._cells)
if (
TableRow.match(source)
and TableRow._is_delimiter
and num_of_cols == len(TableRow._cells)
):
cls._num_of_cols = num_of_cols
lens = [len(x) for x in TableRow._cells]
proportions = [x/sum(lens) for x in lens]
TableRow.proportions = proportions
source.reset()
return True
source.reset()
return False
@classmethod
def parse(cls, source):
rv = cls()
rv._num_of_cols = cls._num_of_cols
rv.children = []
with source.under_state(rv):
TableRow.match(source)
header = TableRow(TableRow.parse(source))
rv.children.append(header)
TableRow.match(source)
delimiters = TableRow._cells
source.consume()
for d, th in zip(delimiters, header.children):
stripped_d = d.strip()
th.header = True
if stripped_d[0] == ":" and stripped_d[-1] == ":":
th.align = "center"
elif stripped_d[0] == ":":
th.align = "left"
elif stripped_d[-1] == ":":
th.align = "right"
while not source.exhausted:
for e in source.parser._build_block_element_list():
if issubclass(e, (Table, block.Paragraph)):
continue
if e.match(source):
break
else:
if TableRow.match(source):
rv.children.append(TableRow(TableRow.parse(source)))
continue
break
_resolve_merge_restarts(rv)
return rv
def _resolve_merge_restarts(table: Table) -> None:
"""Mark restart cells that start a vertical/horizontal merge group."""
rows = table.children
if not rows:
return
n_cols = table._num_of_cols
n_rows = len(rows)
for col in range(n_cols):
for row in range(n_rows):
cell = rows[row].children[col]
if cell.merge_v != "continue":
# restart if any continue below until next non-continue
if row + 1 < n_rows and rows[row + 1].children[col].merge_v == "continue":
cell.merge_v = "restart"
for row in range(n_rows):
for col in range(n_cols):
cell = rows[row].children[col]
if cell.merge_h != "continue":
if col + 1 < n_cols and rows[row].children[col + 1].merge_h == "continue":
cell.merge_h = "restart"
+24
View File
@@ -0,0 +1,24 @@
from marko.block import BlockElement
from marko.source import Source
from re import Match
class TOC(BlockElement):
"""Represents TOC field
Syntax: [TOC]"""
pattern = r"\[(TOC)\]+"
def __init__(self, match: Match[str]):
pass
@classmethod
def match(cls, source: Source) -> Match[str] | None:
return source.expect_re(cls.pattern)
@classmethod
def parse(cls, source: Source) -> Match[str] | None:
m = source.match
source.consume()
return m