90 lines
2.5 KiB
Python
90 lines
2.5 KiB
Python
"""Индексация BSL-модулей конфигурации."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
from dataclasses import dataclass
|
||
from pathlib import Path
|
||
|
||
from .config import OBJECT_TYPE_FOLDERS
|
||
|
||
# Процедура/Функция Имя(...)
|
||
RE_SYMBOL = re.compile(
|
||
r"(?im)^\s*(?:&[А-Яа-яA-Za-z_]+)*\s*"
|
||
r"(?:Процедура|Функция|Procedure|Function)\s+"
|
||
r"([А-Яа-яA-Za-z_][А-Яа-яA-Za-z0-9_]*)"
|
||
)
|
||
|
||
# Лимит тела для FTS (символы) — полные гигантские модули не кладём целиком.
|
||
MAX_BODY_CHARS = 200_000
|
||
|
||
|
||
@dataclass
|
||
class ModuleRec:
|
||
config: str
|
||
rel_path: str
|
||
abs_path: str
|
||
owner: str
|
||
module_kind: str
|
||
symbols: list[str]
|
||
body: str
|
||
line_count: int
|
||
size: int
|
||
mtime_ns: int
|
||
|
||
|
||
def _module_kind(rel: Path) -> str:
|
||
name = rel.name
|
||
if name == "Module.bsl":
|
||
parent = rel.parent.name
|
||
if parent == "Form":
|
||
return "FormModule"
|
||
if parent == "Ext":
|
||
# CommonModules/.../Ext/Module.bsl или HTTPServices/.../Ext/Module.bsl
|
||
return "Module"
|
||
return "Module"
|
||
if name.endswith("Module.bsl"):
|
||
return name[: -len(".bsl")] # ObjectModule, ManagerModule, ...
|
||
return name
|
||
|
||
|
||
def _owner_from_rel(rel: Path) -> str:
|
||
"""Documents/ЗаказКлиента/Ext/ObjectModule.bsl → Document.ЗаказКлиента."""
|
||
parts = rel.parts
|
||
if not parts:
|
||
return ""
|
||
folder = parts[0]
|
||
otype = OBJECT_TYPE_FOLDERS.get(folder)
|
||
if otype and len(parts) >= 2:
|
||
return f"{otype}.{parts[1]}"
|
||
return folder
|
||
|
||
|
||
def parse_bsl_file(path: Path, *, config: str, src_root: Path) -> ModuleRec | None:
|
||
try:
|
||
st = path.stat()
|
||
text = path.read_text(encoding="utf-8-sig", errors="replace")
|
||
except OSError:
|
||
return None
|
||
rel = path.relative_to(src_root).as_posix()
|
||
symbols = sorted(set(RE_SYMBOL.findall(text)))
|
||
body = text if len(text) <= MAX_BODY_CHARS else text[:MAX_BODY_CHARS]
|
||
return ModuleRec(
|
||
config=config,
|
||
rel_path=rel,
|
||
abs_path=str(path),
|
||
owner=_owner_from_rel(Path(rel)),
|
||
module_kind=_module_kind(Path(rel)),
|
||
symbols=symbols,
|
||
body=body,
|
||
line_count=text.count("\n") + (1 if text and not text.endswith("\n") else 0),
|
||
size=st.st_size,
|
||
mtime_ns=getattr(st, "st_mtime_ns", int(st.st_mtime * 1e9)),
|
||
)
|
||
|
||
|
||
def iter_bsl_files(src: Path) -> list[Path]:
|
||
if not src.is_dir():
|
||
return []
|
||
return sorted(src.rglob("*.bsl"))
|