mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-18 08:39:40 +03:00
feat(meta-compile,meta-decompile): батч object-свойств реквизита (консистентно с form) (v1.21/v0.6)
Раундтрип-кластер extra-свойств реквизита. Компилятор хардкодил дефолты, реальные значения варьируются (корпус 32642 реквизита): FullTextSearch DontUse 10531, Comment 7993, Mask 1489, Format/EditFormat 684/686, Use ForFolderAndItem 1299, CreateOnInput/QuickChoice/FillFromFillingValue/ DataHistory. По философии DSL — в объектную форму (shorthand несёт только частое: req/index/multiline). Object-ключи (omit-on-default), имена согласованы с form-compile где применимо: comment, fullTextSearch, mask, format/editFormat (ML), use, createOnInput, quickChoice, dataHistory, fillFromFillingValue, passwordMode, choiceHistoryOnInput. Прощающий ввод: fillCheck→fillChecking (bool true→ShowError), quickChoice bool (true→Use/false→DontUse). Emit-Attribute параметризован; декомпилятор переключается на object-форму при любом непокрытом свойстве. spec v2.6 §4.2 (полная таблица ключей); тест-кейс catalog-attr-props. Валидация: PS==PY паритет; категории батча 0 (FullTextSearch/Comment/Mask/Format/Use/...); полный прогон −68458 (161917→93459); регресс 37/37 (ps+py); 1С-cert зелёный. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -641,8 +641,13 @@ function Parse-AttributeShorthand {
|
||||
return $parsed
|
||||
}
|
||||
|
||||
# Object form. synonym/tooltip — сквозной проброс (строка ИЛИ объект {ru,en}), НЕ стрингифаим.
|
||||
# Object form. synonym/tooltip/format/editFormat — сквозной проброс (строка ИЛИ {ru,en}), НЕ стрингифаим.
|
||||
# fillCheck — синоним fillChecking (из формы; bool true→ShowError). quickChoice — прощаем bool (true→Use, false→DontUse).
|
||||
$name = "$($val.name)"
|
||||
$fc = if ($val.fillChecking) { "$($val.fillChecking)" }
|
||||
elseif ($null -ne $val.fillCheck) { if ($val.fillCheck -is [bool]) { if ($val.fillCheck) { 'ShowError' } else { '' } } else { "$($val.fillCheck)" } }
|
||||
else { "" }
|
||||
$qc = if ($null -ne $val.quickChoice) { if ($val.quickChoice -is [bool]) { if ($val.quickChoice) { 'Use' } else { 'DontUse' } } else { "$($val.quickChoice)" } } else { "" }
|
||||
return @{
|
||||
name = $name
|
||||
type = Build-TypeStr $val
|
||||
@@ -650,10 +655,20 @@ function Parse-AttributeShorthand {
|
||||
tooltip = $val.tooltip
|
||||
comment = if ($val.comment) { "$($val.comment)" } else { "" }
|
||||
flags = @(if ($val.flags) { $val.flags } else { @() })
|
||||
fillChecking = if ($val.fillChecking) { "$($val.fillChecking)" } else { "" }
|
||||
fillChecking = $fc
|
||||
indexing = if ($val.indexing) { "$($val.indexing)" } else { "" }
|
||||
multiLine = if ($val.multiLine -eq $true) { $true } else { $false }
|
||||
choiceHistoryOnInput = if ($val.choiceHistoryOnInput) { "$($val.choiceHistoryOnInput)" } else { "" }
|
||||
fullTextSearch = if ($val.fullTextSearch) { "$($val.fullTextSearch)" } else { "" }
|
||||
fillFromFillingValue = if ($val.fillFromFillingValue -eq $true) { $true } else { $false }
|
||||
createOnInput = if ($val.createOnInput) { "$($val.createOnInput)" } else { "" }
|
||||
quickChoice = $qc
|
||||
dataHistory = if ($val.dataHistory) { "$($val.dataHistory)" } else { "" }
|
||||
use = if ($val.use) { "$($val.use)" } else { "" }
|
||||
passwordMode = if ($val.passwordMode -eq $true) { $true } else { $false }
|
||||
format = $val.format
|
||||
editFormat = $val.editFormat
|
||||
mask = if ($val.mask) { "$($val.mask)" } else { "" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -977,7 +992,7 @@ function Emit-Attribute {
|
||||
X "$indent`t<Properties>"
|
||||
X "$indent`t`t<Name>$(Esc-Xml $parsed.name)</Name>"
|
||||
Emit-MLText "$indent`t`t" "Synonym" $parsed.synonym
|
||||
X "$indent`t`t<Comment/>"
|
||||
if ($parsed.comment) { X "$indent`t`t<Comment>$(Esc-XmlText $parsed.comment)</Comment>" } else { X "$indent`t`t<Comment/>" }
|
||||
|
||||
# Type
|
||||
$typeStr = $parsed.type
|
||||
@@ -990,12 +1005,13 @@ function Emit-Attribute {
|
||||
X "$indent`t`t</Type>"
|
||||
}
|
||||
|
||||
X "$indent`t`t<PasswordMode>false</PasswordMode>"
|
||||
X "$indent`t`t<Format/>"
|
||||
X "$indent`t`t<EditFormat/>"
|
||||
$pwMode = if ($parsed.passwordMode -eq $true) { "true" } else { "false" }
|
||||
X "$indent`t`t<PasswordMode>$pwMode</PasswordMode>"
|
||||
Emit-MLText "$indent`t`t" "Format" $parsed.format
|
||||
Emit-MLText "$indent`t`t" "EditFormat" $parsed.editFormat
|
||||
Emit-MLText "$indent`t`t" "ToolTip" $parsed.tooltip
|
||||
X "$indent`t`t<MarkNegatives>false</MarkNegatives>"
|
||||
X "$indent`t`t<Mask/>"
|
||||
if ($parsed.mask) { X "$indent`t`t<Mask>$(Esc-XmlText $parsed.mask)</Mask>" } else { X "$indent`t`t<Mask/>" }
|
||||
$multiLine = if ($parsed.multiLine -eq $true -or $parsed.flags -contains "multiline") { "true" } else { "false" }
|
||||
X "$indent`t`t<MultiLine>$multiLine</MultiLine>"
|
||||
X "$indent`t`t<ExtendedEdit>false</ExtendedEdit>"
|
||||
@@ -1005,7 +1021,8 @@ function Emit-Attribute {
|
||||
# FillFromFillingValue — not for tabular/processor/chart/register-other
|
||||
# (Chart*, AccumulationRegister/AccountingRegister/CalculationRegister don't support these)
|
||||
if ($context -notin @("tabular", "processor", "chart", "register-other")) {
|
||||
X "$indent`t`t<FillFromFillingValue>false</FillFromFillingValue>"
|
||||
$ffv = if ($parsed.fillFromFillingValue -eq $true) { "true" } else { "false" }
|
||||
X "$indent`t`t<FillFromFillingValue>$ffv</FillFromFillingValue>"
|
||||
}
|
||||
|
||||
# FillValue — same restriction
|
||||
@@ -1022,8 +1039,10 @@ function Emit-Attribute {
|
||||
X "$indent`t`t<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>"
|
||||
X "$indent`t`t<ChoiceParameterLinks/>"
|
||||
X "$indent`t`t<ChoiceParameters/>"
|
||||
X "$indent`t`t<QuickChoice>Auto</QuickChoice>"
|
||||
X "$indent`t`t<CreateOnInput>Auto</CreateOnInput>"
|
||||
$qc = if ($parsed.quickChoice) { $parsed.quickChoice } else { "Auto" }
|
||||
X "$indent`t`t<QuickChoice>$qc</QuickChoice>"
|
||||
$coi = if ($parsed.createOnInput) { $parsed.createOnInput } else { "Auto" }
|
||||
X "$indent`t`t<CreateOnInput>$coi</CreateOnInput>"
|
||||
X "$indent`t`t<ChoiceForm/>"
|
||||
X "$indent`t`t<LinkByType/>"
|
||||
$chi = if ($parsed.choiceHistoryOnInput) { $parsed.choiceHistoryOnInput } else { "Auto" }
|
||||
@@ -1031,7 +1050,8 @@ function Emit-Attribute {
|
||||
|
||||
# Use — only for catalog top-level attributes
|
||||
if ($context -eq "catalog") {
|
||||
X "$indent`t`t<Use>ForItem</Use>"
|
||||
$use = if ($parsed.use) { $parsed.use } else { "ForItem" }
|
||||
X "$indent`t`t<Use>$use</Use>"
|
||||
}
|
||||
|
||||
# Indexing/FullTextSearch/DataHistory — not for non-stored objects (processor, processor-tabular)
|
||||
@@ -1042,10 +1062,12 @@ function Emit-Attribute {
|
||||
if ($parsed.indexing) { $indexing = $parsed.indexing }
|
||||
X "$indent`t`t<Indexing>$indexing</Indexing>"
|
||||
|
||||
X "$indent`t`t<FullTextSearch>Use</FullTextSearch>"
|
||||
$fts = if ($parsed.fullTextSearch) { $parsed.fullTextSearch } else { "Use" }
|
||||
X "$indent`t`t<FullTextSearch>$fts</FullTextSearch>"
|
||||
# DataHistory — not for Chart* types and non-InformationRegister register family
|
||||
if ($context -notin @("chart", "register-other")) {
|
||||
X "$indent`t`t<DataHistory>Use</DataHistory>"
|
||||
$dh = if ($parsed.dataHistory) { $parsed.dataHistory } else { "Use" }
|
||||
X "$indent`t`t<DataHistory>$dh</DataHistory>"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -644,6 +644,17 @@ def parse_attribute_shorthand(val):
|
||||
return parsed
|
||||
# Object form. synonym/tooltip — сквозной проброс (строка ИЛИ dict {ru,en}), НЕ стрингифаим.
|
||||
name = str(val.get('name', ''))
|
||||
# fillCheck — синоним fillChecking (bool true→ShowError). quickChoice — прощаем bool (true→Use, false→DontUse).
|
||||
if val.get('fillChecking'):
|
||||
fc = str(val['fillChecking'])
|
||||
elif val.get('fillCheck') is not None:
|
||||
fc = ('ShowError' if val['fillCheck'] else '') if isinstance(val['fillCheck'], bool) else str(val['fillCheck'])
|
||||
else:
|
||||
fc = ''
|
||||
if val.get('quickChoice') is not None:
|
||||
qc = ('Use' if val['quickChoice'] else 'DontUse') if isinstance(val['quickChoice'], bool) else str(val['quickChoice'])
|
||||
else:
|
||||
qc = ''
|
||||
return {
|
||||
'name': name,
|
||||
'type': build_type_str(val),
|
||||
@@ -651,10 +662,20 @@ def parse_attribute_shorthand(val):
|
||||
'tooltip': val.get('tooltip'),
|
||||
'comment': str(val['comment']) if val.get('comment') else '',
|
||||
'flags': list(val.get('flags', [])),
|
||||
'fillChecking': str(val['fillChecking']) if val.get('fillChecking') else '',
|
||||
'fillChecking': fc,
|
||||
'indexing': str(val['indexing']) if val.get('indexing') else '',
|
||||
'multiLine': True if val.get('multiLine') is True else False,
|
||||
'choiceHistoryOnInput': str(val['choiceHistoryOnInput']) if val.get('choiceHistoryOnInput') else '',
|
||||
'fullTextSearch': str(val['fullTextSearch']) if val.get('fullTextSearch') else '',
|
||||
'fillFromFillingValue': True if val.get('fillFromFillingValue') is True else False,
|
||||
'createOnInput': str(val['createOnInput']) if val.get('createOnInput') else '',
|
||||
'quickChoice': qc,
|
||||
'dataHistory': str(val['dataHistory']) if val.get('dataHistory') else '',
|
||||
'use': str(val['use']) if val.get('use') else '',
|
||||
'passwordMode': True if val.get('passwordMode') is True else False,
|
||||
'format': val.get('format'),
|
||||
'editFormat': val.get('editFormat'),
|
||||
'mask': str(val['mask']) if val.get('mask') else '',
|
||||
}
|
||||
|
||||
def parse_enum_value_shorthand(val):
|
||||
@@ -974,7 +995,10 @@ def emit_attribute(indent, parsed, context):
|
||||
X(f'{indent}\t<Properties>')
|
||||
X(f'{indent}\t\t<Name>{esc_xml(parsed["name"])}</Name>')
|
||||
emit_mltext(f'{indent}\t\t', 'Synonym', parsed['synonym'])
|
||||
X(f'{indent}\t\t<Comment/>')
|
||||
if parsed.get('comment'):
|
||||
X(f'{indent}\t\t<Comment>{esc_xml_text(parsed["comment"])}</Comment>')
|
||||
else:
|
||||
X(f'{indent}\t\t<Comment/>')
|
||||
type_str = parsed['type']
|
||||
if type_str:
|
||||
emit_value_type(f'{indent}\t\t', type_str)
|
||||
@@ -982,12 +1006,16 @@ def emit_attribute(indent, parsed, context):
|
||||
X(f'{indent}\t\t<Type>')
|
||||
X(f'{indent}\t\t\t<v8:Type>xs:string</v8:Type>')
|
||||
X(f'{indent}\t\t</Type>')
|
||||
X(f'{indent}\t\t<PasswordMode>false</PasswordMode>')
|
||||
X(f'{indent}\t\t<Format/>')
|
||||
X(f'{indent}\t\t<EditFormat/>')
|
||||
pw_mode = 'true' if parsed.get('passwordMode') is True else 'false'
|
||||
X(f'{indent}\t\t<PasswordMode>{pw_mode}</PasswordMode>')
|
||||
emit_mltext(f'{indent}\t\t', 'Format', parsed.get('format'))
|
||||
emit_mltext(f'{indent}\t\t', 'EditFormat', parsed.get('editFormat'))
|
||||
emit_mltext(f'{indent}\t\t', 'ToolTip', parsed.get('tooltip'))
|
||||
X(f'{indent}\t\t<MarkNegatives>false</MarkNegatives>')
|
||||
X(f'{indent}\t\t<Mask/>')
|
||||
if parsed.get('mask'):
|
||||
X(f'{indent}\t\t<Mask>{esc_xml_text(parsed["mask"])}</Mask>')
|
||||
else:
|
||||
X(f'{indent}\t\t<Mask/>')
|
||||
multi_line = 'true' if (parsed.get('multiLine') is True or 'multiline' in parsed.get('flags', [])) else 'false'
|
||||
X(f'{indent}\t\t<MultiLine>{multi_line}</MultiLine>')
|
||||
X(f'{indent}\t\t<ExtendedEdit>false</ExtendedEdit>')
|
||||
@@ -996,7 +1024,8 @@ def emit_attribute(indent, parsed, context):
|
||||
# FillFromFillingValue / FillValue — not for tabular/processor/chart/register-other
|
||||
# (Chart*, AccumulationRegister/AccountingRegister/CalculationRegister don't support these)
|
||||
if context not in ('tabular', 'processor', 'chart', 'register-other'):
|
||||
X(f'{indent}\t\t<FillFromFillingValue>false</FillFromFillingValue>')
|
||||
ffv = 'true' if parsed.get('fillFromFillingValue') is True else 'false'
|
||||
X(f'{indent}\t\t<FillFromFillingValue>{ffv}</FillFromFillingValue>')
|
||||
if context not in ('tabular', 'processor', 'chart', 'register-other'):
|
||||
emit_fill_value(f'{indent}\t\t', type_str)
|
||||
fill_checking = 'DontCheck'
|
||||
@@ -1008,14 +1037,14 @@ def emit_attribute(indent, parsed, context):
|
||||
X(f'{indent}\t\t<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>')
|
||||
X(f'{indent}\t\t<ChoiceParameterLinks/>')
|
||||
X(f'{indent}\t\t<ChoiceParameters/>')
|
||||
X(f'{indent}\t\t<QuickChoice>Auto</QuickChoice>')
|
||||
X(f'{indent}\t\t<CreateOnInput>Auto</CreateOnInput>')
|
||||
X(f'{indent}\t\t<QuickChoice>{parsed.get("quickChoice") or "Auto"}</QuickChoice>')
|
||||
X(f'{indent}\t\t<CreateOnInput>{parsed.get("createOnInput") or "Auto"}</CreateOnInput>')
|
||||
X(f'{indent}\t\t<ChoiceForm/>')
|
||||
X(f'{indent}\t\t<LinkByType/>')
|
||||
chi = parsed.get('choiceHistoryOnInput') or 'Auto'
|
||||
X(f'{indent}\t\t<ChoiceHistoryOnInput>{chi}</ChoiceHistoryOnInput>')
|
||||
if context == 'catalog':
|
||||
X(f'{indent}\t\t<Use>ForItem</Use>')
|
||||
X(f'{indent}\t\t<Use>{parsed.get("use") or "ForItem"}</Use>')
|
||||
if context not in ('processor', 'processor-tabular'):
|
||||
indexing = 'DontIndex'
|
||||
if 'index' in parsed.get('flags', []):
|
||||
@@ -1025,10 +1054,10 @@ def emit_attribute(indent, parsed, context):
|
||||
if parsed.get('indexing'):
|
||||
indexing = parsed['indexing']
|
||||
X(f'{indent}\t\t<Indexing>{indexing}</Indexing>')
|
||||
X(f'{indent}\t\t<FullTextSearch>Use</FullTextSearch>')
|
||||
X(f'{indent}\t\t<FullTextSearch>{parsed.get("fullTextSearch") or "Use"}</FullTextSearch>')
|
||||
# DataHistory — not for Chart* types and non-InformationRegister register family
|
||||
if context not in ('chart', 'register-other'):
|
||||
X(f'{indent}\t\t<DataHistory>Use</DataHistory>')
|
||||
X(f'{indent}\t\t<DataHistory>{parsed.get("dataHistory") or "Use"}</DataHistory>')
|
||||
X(f'{indent}\t</Properties>')
|
||||
X(f'{indent}</Attribute>')
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# meta-decompile v0.5 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
|
||||
# meta-decompile v0.6 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
#
|
||||
# Пилот: только Catalog. Инверс meta-compile (omit-on-default: ключ эмитим только
|
||||
@@ -195,18 +195,37 @@ function Attr-ToDsl {
|
||||
if ($ix) { if ($ix.InnerText -eq 'Index') { $flags += 'index' } elseif ($ix.InnerText -eq 'IndexWithAdditionalOrder') { $flags += 'indexAdditional' } }
|
||||
$ml = $ap.SelectSingleNode('md:MultiLine', $nsm); if ($ml -and $ml.InnerText -eq 'true') { $flags += 'multiline' }
|
||||
|
||||
# Синоним/подсказка (строка ru-only ИЛИ {ru,en}). Кастомный синоним ИЛИ наличие подсказки → object-форма.
|
||||
# Синоним/подсказка (строка ru-only ИЛИ {ru,en}).
|
||||
$synVal = Get-MLValue ($ap.SelectSingleNode('md:Synonym', $nsm))
|
||||
$synCustom = $false
|
||||
if ($synVal -is [string]) { if ($synVal -ne (Split-CamelWords $nm)) { $synCustom = $true } }
|
||||
elseif ($null -ne $synVal) { $synCustom = $true } # {ru,en} = всегда кастом
|
||||
$ttVal = Get-MLValue ($ap.SelectSingleNode('md:ToolTip', $nsm))
|
||||
|
||||
if ($synCustom -or ($null -ne $ttVal)) {
|
||||
# Extra-свойства реквизита (omit-on-default). Наличие любого → object-форма.
|
||||
# $en(tag) → InnerText узла или $null.
|
||||
$en = { param($tag) $n = $ap.SelectSingleNode("md:$tag", $nsm); if ($n) { $n.InnerText } else { $null } }
|
||||
$extra = [ordered]@{}
|
||||
$v = & $en 'Comment'; if ($v) { $extra['comment'] = $v }
|
||||
$v = & $en 'FullTextSearch'; if ($v -and $v -ne 'Use') { $extra['fullTextSearch'] = $v }
|
||||
$v = & $en 'FillFromFillingValue'; if ($v -eq 'true') { $extra['fillFromFillingValue'] = $true }
|
||||
$v = & $en 'CreateOnInput'; if ($v -and $v -ne 'Auto') { $extra['createOnInput'] = $v }
|
||||
$v = & $en 'QuickChoice'; if ($v -and $v -ne 'Auto') { $extra['quickChoice'] = $v }
|
||||
$v = & $en 'DataHistory'; if ($v -and $v -ne 'Use') { $extra['dataHistory'] = $v }
|
||||
$v = & $en 'Use'; if ($v -and $v -ne 'ForItem') { $extra['use'] = $v }
|
||||
$v = & $en 'PasswordMode'; if ($v -eq 'true') { $extra['passwordMode'] = $true }
|
||||
$v = & $en 'Mask'; if ($v) { $extra['mask'] = $v }
|
||||
$v = & $en 'ChoiceHistoryOnInput'; if ($v -and $v -ne 'Auto') { $extra['choiceHistoryOnInput'] = $v }
|
||||
$v = & $en 'FillChecking'; if ($v -eq 'ShowWarning') { $extra['fillChecking'] = 'ShowWarning' }
|
||||
$fmtV = Get-MLValue ($ap.SelectSingleNode('md:Format', $nsm)); if ($null -ne $fmtV) { $extra['format'] = $fmtV }
|
||||
$efmtV = Get-MLValue ($ap.SelectSingleNode('md:EditFormat', $nsm)); if ($null -ne $efmtV) { $extra['editFormat'] = $efmtV }
|
||||
|
||||
if ($synCustom -or ($null -ne $ttVal) -or $extra.Count -gt 0) {
|
||||
$o = [ordered]@{ name = $nm }
|
||||
if ($ts) { $o['type'] = $ts }
|
||||
if ($synCustom) { $o['synonym'] = $synVal }
|
||||
if ($null -ne $ttVal) { $o['tooltip'] = $ttVal }
|
||||
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
|
||||
if ($flags.Count -gt 0) { $o['flags'] = [System.Collections.ArrayList]@($flags) }
|
||||
return $o
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user