diff --git a/build_docx.py b/build_docx.py index 8b45111..b1011ac 100644 --- a/build_docx.py +++ b/build_docx.py @@ -7,9 +7,16 @@ 3. Строит DOCX: - Титульная страница (название, подзаголовок, дата) - Оглавление (поле Word TOC — пользователь жмёт правую кнопку → Обновить поле) - - 47 разделов с кодом (заголовок файла + код моноширинным шрифтом на сером фоне) + - разделы с кодом (заголовок файла + код с подсветкой синтаксиса на сером фоне) - Номер страницы в нижнем колонтитуле +Подсветка синтаксиса CFML (4 цвета): + синий — CF-теги (, , , ...) + зелёный — строковые значения ("...", '...') + фиолетовый — выражения в # (#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'|', 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}') diff --git a/results/svc-api-x.docx b/results/svc-api-x.docx index 23b2a1d..65d4616 100644 Binary files a/results/svc-api-x.docx and b/results/svc-api-x.docx differ