feat(meta-compile,meta-decompile): object-списки InputByString/DataLockFields/BasedOn (v1.28/v0.19)

Три object-блока каталога компилятор хардкодил (InputByString={Descr,Code},
DataLockFields/BasedOn пустые), декомпилятор не захватывал.

Class-2 фикс InputByString: хардкод → вывод из Code/DescriptionLength
([Descr при D>0]+[Code при C>0]) — покрывает 88.5% каталогов, убил каскад
Field ADDED=80 (у 1057/1640 InputByString = только Наименование).

Class-3 DSL:
  • inputByString — массив имён полей (авто-резолв через Expand-DataPath,
    как dataPath), [] = пусто; декомпилятор эмитит только при отличии от дефолта.
  • dataLockFields — поля блокировки, omit-on-empty.
  • basedOn — «ввод на основании», MDObjectRef verbatim, omit-on-empty.
Хелперы Emit-FieldBlock/Emit-BasedOn (+py-порт); декомпилятор Short-Field
(частичная форма StandardAttribute.X/Attribute.X для dogfood резолвера).

Class-2 ловушка PS: пустой @() в позиц.параметр схлопывается в $null, а
@($null) = массив из 1 null → пустой <xr:Field>. Фикс — Where-Object фильтр.

Роундтрип 120 объектов: match 20→94, TOTAL 305→183 (−122), DataLockFields
35→0, InputByString/BasedOn=0, новых diff не внесено. Override-кейсы (реверс,
+реквизит, пустой, codeonly, dlf, basedOn) роундтрипятся чисто. Регресс 46/46
ps1+py, ps1↔py identical. spec §7.1.5, кейс catalog-inputbystring-datalock.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-04 14:08:42 +03:00
co-authored by Claude Opus 4.8
parent e95fb6b619
commit 65c4fba471
10 changed files with 622 additions and 15 deletions
@@ -1,4 +1,4 @@
# meta-compile v1.27 — Compile 1C metadata object from JSON
# meta-compile v1.28 — Compile 1C metadata object from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -1202,6 +1202,29 @@ function Emit-LinkByType {
X "$indent</LinkByType>"
}
# Есть ли ключ в $def (отличаем отсутствие от пустого массива [] = явно пусто).
function Test-DefKey { param([string]$name) return ($def.PSObject -and $def.PSObject.Properties -and ($def.PSObject.Properties.Name -contains $name)) }
# <Tag> со списком <xr:Field> (InputByString/DataLockFields). $fields — готовые полные пути. Пусто → self-close.
function Emit-FieldBlock {
param([string]$indent, [string]$tag, $fields)
$arr = @($fields | Where-Object { "$_" -ne '' })
if ($arr.Count -eq 0) { X "$indent<$tag/>"; return }
X "$indent<$tag>"
foreach ($f in $arr) { X "$indent`t<xr:Field>$(Esc-Xml "$f")</xr:Field>" }
X "$indent</$tag>"
}
# <BasedOn> — «ввод на основании», список MDObjectRef ("Catalog.X"/"Document.Y"). Нет ключа/пусто → self-close.
function Emit-BasedOn {
param([string]$indent, $items)
$arr = @($items | Where-Object { $_ })
if ($arr.Count -eq 0) { X "$indent<BasedOn/>"; return }
X "$indent<BasedOn>"
foreach ($it in $arr) { X "$indent`t<xr:Item xsi:type=`"xr:MDObjectRef`">$(Esc-Xml "$it")</xr:Item>" }
X "$indent</BasedOn>"
}
# --- Параметры/связи выбора (порт из form-compile; структура реквизита ⟷ элемента формы совпадает) ---
# Свойство из dict/PSCustomObject по списку синонимов (первый найденный, иначе $null).
@@ -1879,10 +1902,15 @@ function Emit-CatalogProperties {
$choiceMode = Get-EnumProp "ChoiceMode" "choiceMode" "BothWays"
X "$i<QuickChoice>$quickChoice</QuickChoice>"
X "$i<ChoiceMode>$choiceMode</ChoiceMode>"
X "$i<InputByString>"
X "$i`t<xr:Field>Catalog.$objName.StandardAttribute.Description</xr:Field>"
X "$i`t<xr:Field>Catalog.$objName.StandardAttribute.Code</xr:Field>"
X "$i</InputByString>"
# InputByString: override `inputByString` (массив имён, авто-резолв; [] = пусто) ЛИБО дефолт [Descr при D>0]+[Code при C>0].
if (Test-DefKey 'inputByString') {
$ibFields = @($def.inputByString | ForEach-Object { Expand-DataPath "$_" })
} else {
$ibFields = @()
if ([int]$descriptionLength -gt 0) { $ibFields += "Catalog.$objName.StandardAttribute.Description" }
if ([int]$codeLength -gt 0) { $ibFields += "Catalog.$objName.StandardAttribute.Code" }
}
Emit-FieldBlock $i "InputByString" $ibFields
X "$i<SearchStringModeOnInputByString>$(Get-EnumProp 'SearchStringModeOnInputByString' 'searchStringModeOnInputByString' 'Begin')</SearchStringModeOnInputByString>"
X "$i<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>"
X "$i<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>"
@@ -1898,8 +1926,9 @@ function Emit-CatalogProperties {
Emit-FormRef $i "AuxiliaryFolderChoiceForm" $def.auxiliaryFolderChoiceForm
$inclHelp = if (Get-BoolProp "includeHelpInContents" $false) { "true" } else { "false" }
X "$i<IncludeHelpInContents>$inclHelp</IncludeHelpInContents>"
X "$i<BasedOn/>"
X "$i<DataLockFields/>"
Emit-BasedOn $i $def.basedOn
$dlFields = if (Test-DefKey 'dataLockFields') { @($def.dataLockFields | ForEach-Object { Expand-DataPath "$_" }) } else { @() }
Emit-FieldBlock $i "DataLockFields" $dlFields
$dataLockControlMode = Get-EnumProp "DataLockControlMode" "dataLockControlMode" "Automatic"
X "$i<DataLockControlMode>$dataLockControlMode</DataLockControlMode>"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# meta-compile v1.27 — Compile 1C metadata object from JSON
# meta-compile v1.28 — Compile 1C metadata object from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -1269,6 +1269,28 @@ def emit_link_by_type(indent, spec):
X(f'{indent}\t<xr:LinkItem>{li}</xr:LinkItem>')
X(f'{indent}</LinkByType>')
def emit_field_block(indent, tag, fields):
"""<Tag> со списком <xr:Field> (InputByString/DataLockFields). Пусто → self-close."""
arr = [f for f in (fields or []) if str(f) != '']
if not arr:
X(f'{indent}<{tag}/>')
return
X(f'{indent}<{tag}>')
for f in arr:
X(f'{indent}\t<xr:Field>{esc_xml(str(f))}</xr:Field>')
X(f'{indent}</{tag}>')
def emit_based_on(indent, items):
"""<BasedOn> — «ввод на основании», список MDObjectRef. Нет/пусто → self-close."""
arr = [it for it in (items or []) if it]
if not arr:
X(f'{indent}<BasedOn/>')
return
X(f'{indent}<BasedOn>')
for it in arr:
X(f'{indent}\t<xr:Item xsi:type="xr:MDObjectRef">{esc_xml(str(it))}</xr:Item>')
X(f'{indent}</BasedOn>')
# --- Параметры/связи выбора (порт из form-compile) ---
def ch_el_prop(obj, names):
@@ -1879,10 +1901,16 @@ def emit_catalog_properties(indent):
choice_mode = get_enum_prop('ChoiceMode', 'choiceMode', 'BothWays')
X(f'{i}<QuickChoice>{quick_choice}</QuickChoice>')
X(f'{i}<ChoiceMode>{choice_mode}</ChoiceMode>')
X(f'{i}<InputByString>')
X(f'{i}\t<xr:Field>Catalog.{obj_name}.StandardAttribute.Description</xr:Field>')
X(f'{i}\t<xr:Field>Catalog.{obj_name}.StandardAttribute.Code</xr:Field>')
X(f'{i}</InputByString>')
# InputByString: override `inputByString` (массив имён, авто-резолв; [] = пусто) ЛИБО дефолт [Descr при D>0]+[Code при C>0].
if 'inputByString' in defn:
ib_fields = [expand_data_path(str(x)) for x in (defn.get('inputByString') or [])]
else:
ib_fields = []
if int(description_length) > 0:
ib_fields.append(f'Catalog.{obj_name}.StandardAttribute.Description')
if int(code_length) > 0:
ib_fields.append(f'Catalog.{obj_name}.StandardAttribute.Code')
emit_field_block(i, 'InputByString', ib_fields)
X(f'{i}<SearchStringModeOnInputByString>{get_enum_prop("SearchStringModeOnInputByString", "searchStringModeOnInputByString", "Begin")}</SearchStringModeOnInputByString>')
X(f'{i}<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>')
X(f'{i}<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>')
@@ -1898,8 +1926,9 @@ def emit_catalog_properties(indent):
emit_form_ref(i, 'AuxiliaryFolderChoiceForm', defn.get('auxiliaryFolderChoiceForm'))
incl_help = 'true' if get_bool_prop('includeHelpInContents', False) else 'false'
X(f'{i}<IncludeHelpInContents>{incl_help}</IncludeHelpInContents>')
X(f'{i}<BasedOn/>')
X(f'{i}<DataLockFields/>')
emit_based_on(i, defn.get('basedOn'))
dl_fields = [expand_data_path(str(x)) for x in defn.get('dataLockFields', [])] if 'dataLockFields' in defn else []
emit_field_block(i, 'DataLockFields', dl_fields)
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use')