feat(form-decompile,form-compile): InputField MultiLine факт. значение + Shortcut как generic-скаляр

Две находки из корпусного хвоста (свериться помог category-forms.py против rt-24):

- **MultiLine факт. значение** (190 LOST в выборке rt-24): компилятор эмитил и
  декомпилятор ловил только `true`, терялся явный `<MultiLine>false>` (425 в корпусе
  8.3.24; absent 204694, true 5183). Теперь захват/эмиссия true/false при наличии,
  отсутствие = дефолт (как PasswordMode). Эвристика autoMaxWidth (multiLineDefault)
  не затронута — продолжает срабатывать только на true.
- **Shortcut → generic-скаляр** (94 LOST на 86 формах): эмитился/ловился только у
  PictureField (инлайн), терялся на InputField (169), UsualGroup (41),
  RadioButtonField (39), Page (22), Table (3), CheckBoxField (1). Перенёс в
  GENERIC_SCALARS (любой элемент через Emit-Layout/Add-GenericScalars), убрал инлайн
  PictureField. Команда — отдельный путь (§7), не трогаю.

Заодно подтверждено покрытие (category-forms против rt-24 = 0): ControlRepresentation
(1464), ShapeRepresentation (877), PasswordMode (689) — уже generic/факт. значение.
AllowedLength (625) — это cascade типов полей дин-списка (отдельный кластер, не трогаем).

Зеркало py (байт-в-байт). Выборка 73 формы (MultiLine=false + Shortcut на 6 типах
элементов): 0 потерь. Кейсы input-fields (+multiLine:false +shortcut на input),
groups (+shortcut на group), picture-field (shortcut через generic) сертифицированы
в 1С. Регресс 43/43 (ps1+py).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-06-11 16:47:20 +03:00
co-authored by Claude Opus 4.8
parent c4f600a36b
commit b2eaa9e129
8 changed files with 21 additions and 15 deletions
@@ -1,4 +1,4 @@
# form-compile v1.113 — Compile 1C managed form from JSON or object metadata
# form-compile v1.114 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -2978,6 +2978,8 @@ $script:genericScalars = @(
@{ Tag='ChoiceListHeight'; Key='choiceListHeight'; Kind='value' }
@{ Tag='ThreeState'; Key='threeState'; Kind='bool' }
@{ Tag='ScrollOnCompress'; Key='scrollOnCompress'; Kind='bool' }
# Сочетание клавиш — общее свойство (input/group/radio/page/picField/label/table/check; команда — отд. путь, §7)
@{ Tag='Shortcut'; Key='shortcut'; Kind='value' }
)
function Emit-GenericScalars {
@@ -3558,7 +3560,7 @@ function Emit-Input {
X "$inner<TitleLocation>$loc</TitleLocation>"
}
if ($el.multiLine -eq $true) { X "$inner<MultiLine>true</MultiLine>" }
if ($null -ne $el.multiLine) { X "$inner<MultiLine>$(if ($el.multiLine){'true'}else{'false'})</MultiLine>" }
if ($null -ne $el.passwordMode) { X "$inner<PasswordMode>$(if ($el.passwordMode){'true'}else{'false'})</PasswordMode>" }
# ChoiceButton — захват «как есть» (платформа эмитит явное значение; ref-поля выводят сама,
# декомпилятор фиксирует факт. значение). Нет ключа → не эмитим (не додумываем по событию).
@@ -4514,7 +4516,6 @@ function Emit-PictureField {
Emit-ColumnPics -el $el -indent $inner
if ($el.titleLocation) { X "$inner<TitleLocation>$(Map-TitleLoc "$($el.titleLocation)")</TitleLocation>" }
if ($el.hyperlink -eq $true) { X "$inner<Hyperlink>true</Hyperlink>" }
if ($el.shortcut) { X "$inner<Shortcut>$(Esc-Xml "$($el.shortcut)")</Shortcut>" }
Emit-Layout -el $el -indent $inner
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# form-compile v1.113 — Compile 1C managed form from JSON or object metadata
# form-compile v1.114 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -3096,6 +3096,8 @@ GENERIC_SCALARS = [
('ChoiceListHeight', 'choiceListHeight', 'value'),
('ThreeState', 'threeState', 'bool'),
('ScrollOnCompress', 'scrollOnCompress', 'bool'),
# Сочетание клавиш — общее свойство (команда — отдельный путь)
('Shortcut', 'shortcut', 'value'),
]
@@ -3640,8 +3642,8 @@ def emit_input(lines, el, name, eid, indent):
loc = loc_map.get(str(el['titleLocation']), str(el['titleLocation']))
lines.append(f'{inner}<TitleLocation>{loc}</TitleLocation>')
if el.get('multiLine') is True:
lines.append(f'{inner}<MultiLine>true</MultiLine>')
if el.get('multiLine') is not None:
lines.append(f'{inner}<MultiLine>{"true" if el["multiLine"] else "false"}</MultiLine>')
if el.get('passwordMode') is not None:
lines.append(f'{inner}<PasswordMode>{"true" if el["passwordMode"] else "false"}</PasswordMode>')
# ChoiceButton — захват «как есть» (платформа эмитит явное значение; ref-поля выводят сама,
@@ -4237,8 +4239,6 @@ def emit_picture_field(lines, el, name, eid, indent):
lines.append(f'{inner}<TitleLocation>{map_title_loc(el["titleLocation"])}</TitleLocation>')
if el.get('hyperlink') is True:
lines.append(f'{inner}<Hyperlink>true</Hyperlink>')
if el.get('shortcut'):
lines.append(f'{inner}<Shortcut>{esc_xml(str(el["shortcut"]))}</Shortcut>')
emit_layout(lines, el, inner)