feat: подсветка синтаксиса CFML в docx, относительные пути

This commit is contained in:
“Naeel”
2026-05-19 17:52:35 +04:00
parent 1497940812
commit c753f14990
2 changed files with 71 additions and 8 deletions
+71 -8
View File
@@ -7,9 +7,16 @@
3. Строит DOCX:
- Титульная страница (название, подзаголовок, дата)
- Оглавление (поле Word TOC — пользователь жмёт правую кнопку → Обновить поле)
- 47 разделов с кодом (заголовок файла + код моноширинным шрифтом на сером фоне)
- разделы с кодом (заголовок файла + код с подсветкой синтаксиса на сером фоне)
- Номер страницы в нижнем колонтитуле
Подсветка синтаксиса CFML (4 цвета):
синий — CF-теги (<cfif>, <cfset>, </cffunction>, ...)
зелёный — строковые значения ("...", '...')
фиолетовый — выражения в # (#variables#, #func()#)
тёмно-голубой — SQL-ключевые слова (SELECT, FROM, WHERE, JOIN, ...)
чёрный — всё остальное
Запуск:
/home/naeel/svc-api-x/.venv/bin/python build_docx.py
@@ -39,8 +46,8 @@ from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
BUNDLE_PATH = '/home/naeel/svc-api-x/results/source-bundle.md' # входной файл
OUTPUT_PATH = '/home/naeel/svc-api-x/results/svc-api-x.docx' # выходной файл
BUNDLE_PATH = 'results/source-bundle.md' # входной файл
OUTPUT_PATH = 'results/svc-api-x.docx' # выходной файл
# ---------------------------------------------------------------------------
@@ -70,7 +77,61 @@ def parse_bundle(path):
# Вспомогательные функции
# ---------------------------------------------------------------------------
def set_font(run, name='Courier New', size=9, bold=False, color=None):
# ---------------------------------------------------------------------------
# Токенизатор CFML
# ---------------------------------------------------------------------------
# Цвета подсветки (RGB)
COL_TAG = (0x1A, 0x5F, 0xB4) # синий — CF-теги
COL_STR = (0x1E, 0x7E, 0x34) # зелёный — строки
COL_VAR = (0x6F, 0x31, 0x78) # фиолетовый — #переменные#
COL_SQL = (0x0D, 0x5C, 0x73) # тёмно-голубой — SQL
COL_DEF = None # чёрный (default)
_PATTERNS = [
('tag', re.compile(
r'</cf\w+|<cf\w+|</cfscript>|<cfscript>', re.IGNORECASE)),
('var', re.compile(
r'#[^#\n]+#')),
('str', re.compile(
r'"[^"\n]*"|' + r"'[^'\n]*'")),
('sql', re.compile(
r'\b(SELECT|FROM|WHERE|JOIN|LEFT|RIGHT|INNER|OUTER|ON|AND|OR|NOT|'
r'INSERT|INTO|UPDATE|DELETE|SET|GROUP|ORDER|BY|HAVING|LIMIT|OFFSET|'
r'AS|DISTINCT|COUNT|SUM|AVG|MAX|MIN|CAST|COALESCE|CASE|WHEN|THEN|'
r'ELSE|END|IS|NULL|IN|EXISTS|UNION|ALL)\b', re.IGNORECASE)),
]
_COLOR_MAP = {'tag': COL_TAG, 'str': COL_STR, 'var': COL_VAR,
'sql': COL_SQL, None: COL_DEF}
def tokenize_cfml(line):
"""Разбивает строку кода на список (text, color) с подсветкой CFML."""
tokens = []
pos = 0
length = len(line)
while pos < length:
best_match = None
best_kind = None
best_start = length
for kind, pat in _PATTERNS:
m = pat.search(line, pos)
if m and m.start() < best_start:
best_match = m
best_kind = kind
best_start = m.start()
if best_match is None:
tokens.append((line[pos:], COL_DEF))
break
if best_start > pos:
tokens.append((line[pos:best_start], COL_DEF))
tokens.append((best_match.group(0), _COLOR_MAP[best_kind]))
pos = best_match.end()
return tokens
def set_font(run, name='Courier New', size=8, bold=False, color=None):
run.font.name = name
run.font.size = Pt(size)
run.font.bold = bold
@@ -259,11 +320,13 @@ def build(sections):
for idx, line in enumerate(lines):
if idx > 0:
run = code_para.add_run()
br_run = code_para.add_run()
br = OxmlElement('w:br')
run._r.append(br)
run = code_para.add_run(line)
set_font(run, name='Courier New', size=8)
br_run._r.append(br)
set_font(br_run)
for text, color in tokenize_cfml(line):
run = code_para.add_run(text)
set_font(run, color=color)
doc.save(OUTPUT_PATH)
print(f'Готово: {OUTPUT_PATH}')
Binary file not shown.