feat: подсветка синтаксиса CFML в docx, относительные пути
This commit is contained in:
+71
-8
@@ -7,9 +7,16 @@
|
|||||||
3. Строит DOCX:
|
3. Строит DOCX:
|
||||||
- Титульная страница (название, подзаголовок, дата)
|
- Титульная страница (название, подзаголовок, дата)
|
||||||
- Оглавление (поле Word TOC — пользователь жмёт правую кнопку → Обновить поле)
|
- Оглавление (поле 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
|
/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.ns import qn
|
||||||
from docx.oxml import OxmlElement
|
from docx.oxml import OxmlElement
|
||||||
|
|
||||||
BUNDLE_PATH = '/home/naeel/svc-api-x/results/source-bundle.md' # входной файл
|
BUNDLE_PATH = 'results/source-bundle.md' # входной файл
|
||||||
OUTPUT_PATH = '/home/naeel/svc-api-x/results/svc-api-x.docx' # выходной файл
|
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.name = name
|
||||||
run.font.size = Pt(size)
|
run.font.size = Pt(size)
|
||||||
run.font.bold = bold
|
run.font.bold = bold
|
||||||
@@ -259,11 +320,13 @@ def build(sections):
|
|||||||
|
|
||||||
for idx, line in enumerate(lines):
|
for idx, line in enumerate(lines):
|
||||||
if idx > 0:
|
if idx > 0:
|
||||||
run = code_para.add_run()
|
br_run = code_para.add_run()
|
||||||
br = OxmlElement('w:br')
|
br = OxmlElement('w:br')
|
||||||
run._r.append(br)
|
br_run._r.append(br)
|
||||||
run = code_para.add_run(line)
|
set_font(br_run)
|
||||||
set_font(run, name='Courier New', size=8)
|
for text, color in tokenize_cfml(line):
|
||||||
|
run = code_para.add_run(text)
|
||||||
|
set_font(run, color=color)
|
||||||
|
|
||||||
doc.save(OUTPUT_PATH)
|
doc.save(OUTPUT_PATH)
|
||||||
print(f'Готово: {OUTPUT_PATH}')
|
print(f'Готово: {OUTPUT_PATH}')
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user