feat(form-decompile,form-compile): Table headerHeight/footerHeight/currentRowUse + форм. conversationsRepresentation (pass-through)

Раундтрип терял 4 свойства (категория Table-скаляры + форм-уровень):
- Table <HeaderHeight>/<FooterHeight> (высота шапки/подвала в строках; ~35/~6 форм)
- Table <CurrentRowUse> (использование текущей строки; ≠ одноимённое свойство команды,
  у которой свой путь захвата/эмиссии). Значения: DontUse/Use/SelectionPresentation/
  SelectionPresentationAndChoice/Choice
- форм-уровень <ConversationsRepresentation> (Auto/Show/DontShow; редкое)

Все три Table-свойства были явно отложены в Emit-Table (комментарий о «строгом Table-XSD»).
Корпусные данные показывают, что 1С эмитит те же теги в РАЗНЫХ позициях у разных форм →
загрузчик толерантен к порядку детей Table (как и существующий компилятор с ранним DataPath).
Размещены pass-through в Emit-Table (height-теги рядом с UseAlternationRowColor, CurrentRowUse
у блока дин-списка); форм-уровень — generic Emit-Properties (авто-PascalCase).

decompile (ps1): захват headerHeight/footerHeight/currentRowUse на Table; ConversationsRepresentation
в KNOWN_FORM_PROPS. compile (ps1+py): эмиссия в emit_table.

Верификация: таргет-раундтрип 4 форм → match (TOTAL diff lines 0); регресс form-compile 43/43
(ps1+py); 1С-cert кейса table (форма с тремя тегами грузится в платформу). spec обновлён.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-06-12 11:09:15 +03:00
parent 6e4fdb443a
commit 6377cdfba5
6 changed files with 32 additions and 7 deletions
@@ -1,4 +1,4 @@
# form-compile v1.125 — Compile 1C managed form from JSON or object metadata
# form-compile v1.126 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -3026,8 +3026,8 @@ $script:genericScalars = @(
@{ Tag='Shortcut'; Key='shortcut'; Kind='value' }
# Батч простых скаляров (input/radio/group/picDecoration/button): режим выбора незаполненного,
# равная ширина колонок, выравнивание детей, масштаб/зум картинки, форма/положение картинки кнопки.
# (Table HeaderHeight/FooterHeight/CurrentRowUse — НЕ здесь: строгий Table-XSD требует точной
# позиции, generic-позиция ломает загрузку; отдельная задача в Emit-Table.)
# (Table HeaderHeight/FooterHeight/CurrentRowUse — НЕ здесь, а в Emit-Table: pass-through,
# 1С толерантна к порядку детей Table — в корпусе те же теги встречаются в разных позициях.)
@{ Tag='IncompleteChoiceMode'; Key='incompleteChoiceMode'; Kind='value' }
@{ Tag='EqualColumnsWidth'; Key='equalColumnsWidth'; Kind='bool' }
@{ Tag='ChildrenAlign'; Key='childrenAlign'; Kind='value' }
@@ -4305,6 +4305,9 @@ function Emit-Table {
if ($el.multipleChoice -eq $true) { X "$inner<MultipleChoice>true</MultipleChoice>" }
if ($el.searchOnInput) { X "$inner<SearchOnInput>$($el.searchOnInput)</SearchOnInput>" }
if ($null -ne $el.markIncomplete) { X "$inner<AutoMarkIncomplete>$(if ($el.markIncomplete){'true'}else{'false'})</AutoMarkIncomplete>" }
# Высота шапки/подвала в строках (pass-through; 1С толерантна к порядку детей Table)
if ($null -ne $el.headerHeight) { X "$inner<HeaderHeight>$($el.headerHeight)</HeaderHeight>" }
if ($null -ne $el.footerHeight) { X "$inner<FooterHeight>$($el.footerHeight)</FooterHeight>" }
if ($el.useAlternationRowColor -eq $true) { X "$inner<UseAlternationRowColor>true</UseAlternationRowColor>" }
if ($el.selectionMode) { X "$inner<SelectionMode>$($el.selectionMode)</SelectionMode>" }
if ($el.rowSelectionMode) { X "$inner<RowSelectionMode>$($el.rowSelectionMode)</RowSelectionMode>" }
@@ -4315,6 +4318,8 @@ function Emit-Table {
if ($el.rowPictureDataPath) { X "$inner<RowPictureDataPath>$($el.rowPictureDataPath)</RowPictureDataPath>" }
# RowsPicture — та же конвенция, что ValuesPicture (дефолт LoadTransparent=false; abs/TransparentPixel)
Emit-PictureRef -val $el.rowsPicture -picTag 'RowsPicture' -indent $inner
# Использование текущей строки таблицы (pass-through; в корпусе соседствует с блоком дин-списка)
if ($el.currentRowUse) { X "$inner<CurrentRowUse>$($el.currentRowUse)</CurrentRowUse>" }
# Блок свойств дин-список-таблицы (помечена эвристикой 11b.4)
if ($el.PSObject.Properties["_dynList"] -and $el._dynList) { Emit-DynListTableBlock -el $el -indent $inner }
if ($el.viewStatusLocation) { X "$inner<ViewStatusLocation>$($el.viewStatusLocation)</ViewStatusLocation>" }
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# form-compile v1.125 — Compile 1C managed form from JSON or object metadata
# form-compile v1.126 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -4029,6 +4029,11 @@ def emit_table(lines, el, name, eid, indent):
lines.append(f'{inner}<SearchOnInput>{el["searchOnInput"]}</SearchOnInput>')
if el.get('markIncomplete') is not None:
lines.append(f'{inner}<AutoMarkIncomplete>{"true" if el["markIncomplete"] else "false"}</AutoMarkIncomplete>')
# Высота шапки/подвала в строках (pass-through; 1С толерантна к порядку детей Table)
if el.get('headerHeight') is not None:
lines.append(f'{inner}<HeaderHeight>{el["headerHeight"]}</HeaderHeight>')
if el.get('footerHeight') is not None:
lines.append(f'{inner}<FooterHeight>{el["footerHeight"]}</FooterHeight>')
if el.get('useAlternationRowColor') is True:
lines.append(f'{inner}<UseAlternationRowColor>true</UseAlternationRowColor>')
if el.get('selectionMode'):
@@ -4047,6 +4052,9 @@ def emit_table(lines, el, name, eid, indent):
lines.append(f'{inner}<RowPictureDataPath>{el["rowPictureDataPath"]}</RowPictureDataPath>')
# RowsPicture — та же конвенция, что ValuesPicture (дефолт LoadTransparent=false; abs/TransparentPixel)
emit_picture_ref(lines, el.get('rowsPicture'), 'RowsPicture', inner)
# Использование текущей строки таблицы (pass-through; в корпусе соседствует с блоком дин-списка)
if el.get('currentRowUse'):
lines.append(f'{inner}<CurrentRowUse>{el["currentRowUse"]}</CurrentRowUse>')
# Блок свойств дин-список-таблицы (помечена эвристикой)
if el.get('_dynList'):
emit_dynlist_table_block(lines, el, inner)
@@ -1,4 +1,4 @@
# form-decompile v0.99 — Decompile 1C managed Form.xml to JSON DSL (draft)
# form-decompile v1.00 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -1761,6 +1761,11 @@ function Decompile-Element {
if ((Get-Child $node 'Footer') -eq 'true') { $obj['footer'] = $true }
# Высота в строках — отдельный ключ heightInTableRows (≠ height = <Height>, его ловит Add-Layout)
$htr = Get-Child $node 'HeightInTableRows'; if ($htr) { $obj['heightInTableRows'] = [int]$htr }
# Высота шапки/подвала таблицы в строках (pass-through; компилятор эмитит в Emit-Table)
$hh = Get-Child $node 'HeaderHeight'; if ($null -ne $hh) { $obj['headerHeight'] = [int]$hh }
$fh = Get-Child $node 'FooterHeight'; if ($null -ne $fh) { $obj['footerHeight'] = [int]$fh }
# Использование текущей строки (Table-уровень; ≠ command-level CurrentRowUse) — pass-through
$cru = Get-Child $node 'CurrentRowUse'; if ($cru) { $obj['currentRowUse'] = $cru }
# CommandBarLocation: для дин-список-таблицы компилятор авто-инжектит "None" → инвертируем
# (нет тега → суппресс-маркер ""; "None" → опускаем = авто-дефолт; иначе → захват).
$cbl = Get-Child $node 'CommandBarLocation'
@@ -2187,7 +2192,7 @@ $titleNode = $root.SelectSingleNode("lf:Title", $ns)
if ($titleNode) { $t = Get-LangText $titleNode; if ($null -ne $t) { $dsl['title'] = $t } }
# properties (прямые скаляры под <Form>, PascalCase → camelCase)
$KNOWN_FORM_PROPS = @('AutoTitle','ReportResult','DetailsData','ReportFormType','AutoShowState','ReportResultViewMode','ViewModeApplicationOnSetReportResult','WindowOpeningMode','CommandBarLocation','SaveDataInSettings','AutoSaveDataInSettings','AutoTime','UsePostingMode','RepostOnWrite','AutoURL','AutoFillCheck','Customizable','EnterKeyBehavior','VerticalScroll','Width','Height','Group','UseForFoldersAndItems','SaveWindowSettings','ScalingMode','VerticalSpacing','VariantAppearance','ShowCloseButton','HorizontalAlign','ChildrenAlign','ShowTitle')
$KNOWN_FORM_PROPS = @('AutoTitle','ReportResult','DetailsData','ReportFormType','AutoShowState','ReportResultViewMode','ViewModeApplicationOnSetReportResult','WindowOpeningMode','CommandBarLocation','SaveDataInSettings','AutoSaveDataInSettings','AutoTime','UsePostingMode','RepostOnWrite','AutoURL','AutoFillCheck','Customizable','EnterKeyBehavior','VerticalScroll','Width','Height','Group','UseForFoldersAndItems','SaveWindowSettings','ScalingMode','VerticalSpacing','VariantAppearance','ShowCloseButton','HorizontalAlign','ChildrenAlign','ShowTitle','ConversationsRepresentation')
$props = [ordered]@{}
foreach ($pn in $KNOWN_FORM_PROPS) {
$v = Get-Child $root $pn
+4
View File
@@ -78,6 +78,7 @@
| `horizontalAlign` | `<HorizontalAlign>` | `Left`, `Center`, `Right` — горизонтальное выравнивание формы |
| `childrenAlign` | `<ChildrenAlign>` | Выравнивание элементов/заголовков (`ItemsLeftTitlesLeft`, `ItemsRightTitlesLeft`, `None`, …) |
| `showTitle` | `<ShowTitle>` | `true` / `false` — показывать заголовок формы |
| `conversationsRepresentation` | `<ConversationsRepresentation>` | `Auto`, `Show`, `DontShow` — отображение панели обсуждений; pass-through (редкое) |
Нераспознанные ключи преобразуются с автоматическим PascalCase (первая буква в верхний регистр).
@@ -560,6 +561,9 @@ companion-панели с собственным контентом. Оба не
| `heightInTableRows` | int | Высота в строках (`<HeightInTableRows>`) — отдельное свойство от `height`; таблица может нести оба |
| `header` | bool | Показывать шапку |
| `footer` | bool | Показывать подвал |
| `headerHeight` | int | Высота шапки в строках (`<HeaderHeight>`); pass-through (редкое, ~35 форм в корпусе) |
| `footerHeight` | int | Высота подвала в строках (`<FooterHeight>`); pass-through (редкое, ~6 форм) |
| `currentRowUse` | string | Использование текущей строки таблицы (`<CurrentRowUse>`): `DontUse`, `Use`, `SelectionPresentation`, `SelectionPresentationAndChoice`, `Choice`; pass-through (≠ одноимённое свойство команды) |
| `commandBarLocation` | string | `None`, `Top`, `Bottom`, `Auto` |
| `searchStringLocation` | string | `None`, `Top`, `Bottom`, `CommandBar`, `Auto` |
| `viewStatusLocation` | string | `None`, `Top`, `Bottom`, `Auto` |
@@ -18,6 +18,9 @@
<MultipleChoice>true</MultipleChoice>
<SearchOnInput>Use</SearchOnInput>
<AutoMarkIncomplete>true</AutoMarkIncomplete>
<HeaderHeight>2</HeaderHeight>
<FooterHeight>1</FooterHeight>
<CurrentRowUse>SelectionPresentationAndChoice</CurrentRowUse>
<ViewStatusLocation>None</ViewStatusLocation>
<SearchControlLocation>None</SearchControlLocation>
<CommandSet>
+1 -1
View File
@@ -16,7 +16,7 @@
"input": {
"title": "Просмотр данных",
"elements": [
{ "table": "Данные", "path": "Данные", "changeRowSet": true, "titleLocation": "top", "height": 80, "heightInTableRows": 5, "autofill": true, "multipleChoice": true, "searchOnInput": "Use", "markIncomplete": true, "settingsNamedItemDetailedRepresentation": false, "heightControlVariant": "UseHeightInTableRows", "maxRowsCount": 5, "autoMaxRowsCount": false,
{ "table": "Данные", "path": "Данные", "changeRowSet": true, "titleLocation": "top", "height": 80, "heightInTableRows": 5, "headerHeight": 2, "footerHeight": 1, "currentRowUse": "SelectionPresentationAndChoice", "autofill": true, "multipleChoice": true, "searchOnInput": "Use", "markIncomplete": true, "settingsNamedItemDetailedRepresentation": false, "heightControlVariant": "UseHeightInTableRows", "maxRowsCount": 5, "autoMaxRowsCount": false,
"viewStatusLocation": "None", "searchControlLocation": "None",
"excludedCommands": ["Add", "Delete", "MoveUp", "MoveDown"],
"commandBar": { "autofill": false, "children": [