Model 1C modules as object parts
CI / python (push) Has been cancelled
CI / rust (push) Has been cancelled

This commit is contained in:
2026-05-17 18:43:43 +03:00
parent 1ad103b6dc
commit 35dd134ebc
7 changed files with 298 additions and 32 deletions
@@ -1095,9 +1095,15 @@ def _attach_bsl_modules(root: Path, normalized: NormalizedProject) -> None:
"original_hash": source.original_hash,
"source_text": source.text,
"module_role": role,
"owner_qualified_name": owner.qualified_name,
"owner_kind": owner.object_kind,
"object_part": _module_object_part(role, form_name),
}
if form_name:
attributes["form_name"] = form_name
form = _find_owner_form(owner, form_name)
if form is not None:
attributes["form_qualified_name"] = form.qualified_name
owner.modules.append(
Module(
name=source_file.stem,
@@ -1189,6 +1195,29 @@ def _module_qualified_name(owner: MetadataObject, role: str, form_name: str, mod
return f"{owner.qualified_name}.{role_suffix}"
def _module_object_part(role: str, form_name: str = "") -> str:
return {
"OBJECT_MODULE": "object.module",
"MANAGER_MODULE": "object.manager",
"RECORD_SET_MODULE": "object.record_set",
"FORM_MODULE": f"form.{form_name}.module" if form_name else "form.module",
"MODULE": "module",
}.get(role, "module")
def _find_owner_form(owner: MetadataObject, form_name: str) -> Form | None:
normalized = form_name.casefold()
return next(
(
form
for form in owner.forms
if form.name.casefold() == normalized
or str(form.qualified_name or "").casefold().endswith(f".{normalized}")
),
None,
)
def _form_name_for_module(root: Path, source_file: Path) -> str:
parts = list(_relative_path(source_file, root).parts)
normalized_parts = [_normalize_path_part(part) for part in parts]
@@ -354,7 +354,7 @@ def index_project(path: str | Path, *, project_id: str | None = None, structure_
}:
parent_by_prefix[node.qualified_name] = node
edges.extend(_link_metadata_to_modules(root, module_nodes, metadata_nodes))
edges.extend(_link_metadata_to_modules(root, module_nodes, metadata_nodes, form_nodes))
edges.extend(_link_role_rights(nodes, role_rights))
edges.extend(_link_scheduled_jobs_to_routines(scheduled_job_nodes, routine_by_name))
edges.extend(_link_commands_to_handlers(command_nodes, routine_by_name))
@@ -1025,6 +1025,7 @@ def _link_metadata_to_modules(
root: Path,
module_nodes: dict[str, SemanticNode],
metadata_nodes: list[SemanticNode],
form_nodes: list[SemanticNode],
) -> list[SemanticEdge]:
if not metadata_nodes:
return []
@@ -1034,6 +1035,7 @@ def _link_metadata_to_modules(
(node.kind, _normalize_lookup_key(node.name)): node
for node in metadata_nodes
}
forms_by_qualified = {_normalize_lookup_key(node.qualified_name): node for node in form_nodes}
edges: list[SemanticEdge] = []
for source_path, module in module_nodes.items():
@@ -1042,6 +1044,26 @@ def _link_metadata_to_modules(
if owner is None:
continue
line = module.source_ref.line_start or 1
module_role = _module_role(source_file)
form_name = _form_name_for_module(root, source_file)
object_part = _module_object_part(module_role, form_name)
module.attributes.update(
{
"owner_lineage_id": owner.lineage_id,
"owner_qualified_name": owner.qualified_name,
"owner_kind": owner.kind.value,
"object_part": object_part,
"module_role": module_role,
}
)
if form_name:
module.attributes["form_name"] = form_name
edge_attributes = {
"link_type": "METADATA_MODULE",
"module_role": module_role,
"object_part": object_part,
"form_name": form_name,
}
edges.append(
_edge(
EdgeKind.CONTAINS,
@@ -1049,16 +1071,61 @@ def _link_metadata_to_modules(
module,
source_path,
line,
{
"link_type": "METADATA_MODULE",
"module_role": _module_role(source_file),
"form_name": _form_name_for_module(root, source_file),
},
edge_attributes,
)
)
if module_role == "FORM_MODULE" and form_name:
form_node = _find_form_node_for_module(owner, form_name, forms_by_qualified)
if form_node is not None:
module.attributes["form_lineage_id"] = form_node.lineage_id
module.attributes["form_qualified_name"] = form_node.qualified_name
edges.append(
_edge(
EdgeKind.CONTAINS,
form_node,
module,
source_path,
line,
{**edge_attributes, "link_type": "FORM_MODULE"},
)
)
return edges
def _find_form_node_for_module(
owner: SemanticNode,
form_name: str,
forms_by_qualified: dict[str, SemanticNode],
) -> SemanticNode | None:
candidates = [
f"{owner.qualified_name}.{form_name}",
f"{owner.qualified_name}.Форма.{form_name}",
]
for candidate in candidates:
form = forms_by_qualified.get(_normalize_lookup_key(candidate))
if form is not None:
return form
suffix = f".{form_name}".casefold()
return next(
(
form
for key, form in forms_by_qualified.items()
if key.endswith(suffix) and key.startswith(_normalize_lookup_key(owner.qualified_name))
),
None,
)
def _module_object_part(module_role: str, form_name: str = "") -> str:
return {
"OBJECT_MODULE": "object.module",
"MANAGER_MODULE": "object.manager",
"RECORD_SET_MODULE": "object.record_set",
"FORM_MODULE": f"form.{form_name}.module" if form_name else "form.module",
"MODULE": "module",
}.get(module_role, "module")
def _link_role_rights(nodes: list[SemanticNode], role_rights: list[dict]) -> list[SemanticEdge]:
if not role_rights:
return []