From b2eaa9e1291dbae50d857213174274f4abff5187 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Thu, 11 Jun 2026 16:47:20 +0300 Subject: [PATCH] =?UTF-8?q?feat(form-decompile,form-compile):=20InputField?= =?UTF-8?q?=20MultiLine=20=D1=84=D0=B0=D0=BA=D1=82.=20=D0=B7=D0=BD=D0=B0?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20+=20Shortcut=20=D0=BA=D0=B0?= =?UTF-8?q?=D0=BA=20generic-=D1=81=D0=BA=D0=B0=D0=BB=D1=8F=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Две находки из корпусного хвоста (свериться помог category-forms.py против rt-24): - **MultiLine факт. значение** (190 LOST в выборке rt-24): компилятор эмитил и декомпилятор ловил только `true`, терялся явный `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) --- .claude/skills/form-compile/scripts/form-compile.ps1 | 7 ++++--- .claude/skills/form-compile/scripts/form-compile.py | 10 +++++----- .../skills/form-decompile/scripts/form-decompile.ps1 | 8 +++++--- docs/form-dsl-spec.md | 4 ++-- tests/skills/cases/form-compile/groups.json | 2 +- tests/skills/cases/form-compile/input-fields.json | 2 +- .../DataProcessors/СГруппами/Forms/Форма/Ext/Form.xml | 1 + .../DataProcessors/ПоляВвода/Forms/Форма/Ext/Form.xml | 2 ++ 8 files changed, 21 insertions(+), 15 deletions(-) diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1 index 20157c16..298b7fb7 100644 --- a/.claude/skills/form-compile/scripts/form-compile.ps1 +++ b/.claude/skills/form-compile/scripts/form-compile.ps1 @@ -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$loc" } - if ($el.multiLine -eq $true) { X "$innertrue" } + if ($null -ne $el.multiLine) { X "$inner$(if ($el.multiLine){'true'}else{'false'})" } if ($null -ne $el.passwordMode) { X "$inner$(if ($el.passwordMode){'true'}else{'false'})" } # ChoiceButton — захват «как есть» (платформа эмитит явное значение; ref-поля выводят сама, # декомпилятор фиксирует факт. значение). Нет ключа → не эмитим (не додумываем по событию). @@ -4514,7 +4516,6 @@ function Emit-PictureField { Emit-ColumnPics -el $el -indent $inner if ($el.titleLocation) { X "$inner$(Map-TitleLoc "$($el.titleLocation)")" } if ($el.hyperlink -eq $true) { X "$innertrue" } - if ($el.shortcut) { X "$inner$(Esc-Xml "$($el.shortcut)")" } Emit-Layout -el $el -indent $inner diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py index 0497a18d..97880515 100644 --- a/.claude/skills/form-compile/scripts/form-compile.py +++ b/.claude/skills/form-compile/scripts/form-compile.py @@ -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}{loc}') - if el.get('multiLine') is True: - lines.append(f'{inner}true') + if el.get('multiLine') is not None: + lines.append(f'{inner}{"true" if el["multiLine"] else "false"}') if el.get('passwordMode') is not None: lines.append(f'{inner}{"true" if el["passwordMode"] else "false"}') # ChoiceButton — захват «как есть» (платформа эмитит явное значение; ref-поля выводят сама, @@ -4237,8 +4239,6 @@ def emit_picture_field(lines, el, name, eid, indent): lines.append(f'{inner}{map_title_loc(el["titleLocation"])}') if el.get('hyperlink') is True: lines.append(f'{inner}true') - if el.get('shortcut'): - lines.append(f'{inner}{esc_xml(str(el["shortcut"]))}') emit_layout(lines, el, inner) diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1 index 2b518262..132bfc5b 100644 --- a/.claude/skills/form-decompile/scripts/form-decompile.ps1 +++ b/.claude/skills/form-decompile/scripts/form-decompile.ps1 @@ -1,4 +1,4 @@ -# form-decompile v0.89 — Decompile 1C managed Form.xml to JSON DSL (draft) +# form-decompile v0.90 — Decompile 1C managed Form.xml to JSON DSL (draft) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью. param( @@ -1304,6 +1304,8 @@ $GENERIC_SCALARS = @( @{ Tag='ChoiceListHeight'; Key='choiceListHeight'; Kind='value' } @{ Tag='ThreeState'; Key='threeState'; Kind='bool' } @{ Tag='ScrollOnCompress'; Key='scrollOnCompress'; Kind='bool' } + # Сочетание клавиш — общее свойство элемента (команда — отдельный путь) + @{ Tag='Shortcut'; Key='shortcut'; Kind='value' } ) # Захват generic-скаляров. Специфичная обработка (если ключ уже задан) — побеждает. @@ -1561,7 +1563,8 @@ function Decompile-Element { $obj[$key] = $name $dp = Get-Child $node 'DataPath'; if ($dp) { $obj['path'] = $dp } Add-CommonProps $obj $node $name - if ((Get-Child $node 'MultiLine') -eq 'true') { $obj['multiLine'] = $true } + # MultiLine: факт. значение (платформа эмитит и явный false — 425 в корпусе; ≠ «if true») + $mlIn = Get-Child $node 'MultiLine'; if ($null -ne $mlIn) { $obj['multiLine'] = ($mlIn -eq 'true') } # PasswordMode: факт. значение (платформа эмитит и false — 349/504 в корпусе; ≠ «if true») $pmIn = Get-Child $node 'PasswordMode'; if ($null -ne $pmIn) { $obj['passwordMode'] = ($pmIn -eq 'true') } $mi = Get-Child $node 'AutoMarkIncomplete'; if ($null -ne $mi) { $obj['markIncomplete'] = ($mi -eq 'true') } @@ -1669,7 +1672,6 @@ function Decompile-Element { $em = Get-Child $node 'EditMode'; if ($em) { $obj['editMode'] = $em } $tl = Get-Child $node 'TitleLocation'; if ($tl) { $obj['titleLocation'] = $tl.ToLower() } if ((Get-Child $node 'Hyperlink') -eq 'true') { $obj['hyperlink'] = $true } - $sct = Get-Child $node 'Shortcut'; if ($sct) { $obj['shortcut'] = $sct } $vp = Get-PictureRef $node 'ValuesPicture'; if ($null -ne $vp) { $obj['valuesPicture'] = $vp } $npt = $node.SelectSingleNode("lf:NonselectedPictureText", $ns); if ($npt) { $t = Get-LangText $npt; if ($null -ne $t) { $obj['nonselectedPictureText'] = $t } } } diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md index d7d1d843..6506a999 100644 --- a/docs/form-dsl-spec.md +++ b/docs/form-dsl-spec.md @@ -371,7 +371,7 @@ companion-панели с собственным контентом. Оба не | Свойство | Тип | Описание | |----------|-----|----------| | `path` | string | DataPath | -| `multiLine` | bool | Многострочный режим | +| `multiLine` | bool | Многострочный режим (``). Факт. значение: платформа эмитит и явный `false` (425 в корпусе) — захватываем true/false, отсутствие = дефолт (однострочный) | | `passwordMode` | bool | Режим пароля | | `titleLocation` | string | `none`, `left`, `right`, `top`, `bottom` | | `choiceButton` | bool | Показывать кнопку выбора | @@ -698,7 +698,7 @@ Pages поддерживает `pagesRepresentation`: `None`, `TabsOnTop`, `Tabs | `valuesPicture` | string \| object | Картинка значения. Формат картинки-ссылки — см. §4.1 «Картинка-ссылка» | | `editMode` | string | Режим редактирования колонки (`EnterOnInput` и т.п.) | | `hyperlink` | bool | Картинка-гиперссылка (`true`) — кликабельная картинка | -| `shortcut` | string | Сочетание клавиш (``, напр. `Ctrl+S`) | +| `shortcut` | string | Сочетание клавиш (``, напр. `Ctrl+S`). Общий generic-скаляр любого элемента (input/group/radio/page/picField/label/table/check), не только колонки-картинки | #### calendar — CalendarField diff --git a/tests/skills/cases/form-compile/groups.json b/tests/skills/cases/form-compile/groups.json index f9ef6346..bc17985a 100644 --- a/tests/skills/cases/form-compile/groups.json +++ b/tests/skills/cases/form-compile/groups.json @@ -17,7 +17,7 @@ "title": "Группы", "elements": [ { "cmdBar": "КоманднаяПанель", "autofill": true }, - { "group": "horizontal", "name": "ГруппаШапка", "behavior": "usual", "showTitle": true, "title": "Шапка", "horizontalStretch": true, "groupHorizontalAlign": "Right", "throughAlign": "Use", "verticalAlign": "Top", "childItemsWidth": "Equal", "horizontalSpacing": "None", "children": [ + { "group": "horizontal", "name": "ГруппаШапка", "behavior": "usual", "showTitle": true, "title": "Шапка", "shortcut": "Ctrl+G", "horizontalStretch": true, "groupHorizontalAlign": "Right", "throughAlign": "Use", "verticalAlign": "Top", "childItemsWidth": "Equal", "horizontalSpacing": "None", "children": [ { "input": "Поле1", "path": "Поле1", "title": "Поле 1", "width": 20, "skipOnInput": true, "mask": "999-999" }, { "input": "Поле2", "path": "Поле2", "title": "Поле 2" }, { "labelField": "Метка", "path": "Поле1", "groupVerticalAlign": "Center" } diff --git a/tests/skills/cases/form-compile/input-fields.json b/tests/skills/cases/form-compile/input-fields.json index a225912b..4e62789a 100644 --- a/tests/skills/cases/form-compile/input-fields.json +++ b/tests/skills/cases/form-compile/input-fields.json @@ -16,7 +16,7 @@ "input": { "title": "Поля ввода", "elements": [ - { "input": "ОбычноеПоле", "path": "ОбычноеПоле", "title": "Обычное поле", "tooltip": "Введите значение поля", "tooltipRepresentation": "ShowBottom", "editMode": "EnterOnInput", "horizontalStretch": false, "verticalStretch": false, "format": "ЧДЦ=2", "editFormat": "ЧДЦ=2; ЧН=", "extendedTooltip": { "width": 30, "autoMaxWidth": false, "text": "Расширенная подсказка поля" } }, + { "input": "ОбычноеПоле", "path": "ОбычноеПоле", "title": "Обычное поле", "tooltip": "Введите значение поля", "tooltipRepresentation": "ShowBottom", "editMode": "EnterOnInput", "multiLine": false, "shortcut": "Ctrl+Shift+O", "horizontalStretch": false, "verticalStretch": false, "format": "ЧДЦ=2", "editFormat": "ЧДЦ=2; ЧН=", "extendedTooltip": { "width": 30, "autoMaxWidth": false, "text": "Расширенная подсказка поля" } }, { "labelField": "Ссылка", "path": "ОбычноеПоле", "titleLocation": "left", "hyperlink": true, "format": { "ru": "ДЛФ=D", "en": "DLF=D" }, "warningOnEdit": "Поле только для чтения" }, { "input": "МногострочноеПоле", "path": "МногострочноеПоле", "multiLine": true, "height": 5, "title": "Комментарий", "wrap": false, "showInHeader": false, "showInFooter": false, "autoCellHeight": true, "footerHorizontalAlign": "Right", "openButton": false, "chooseType": false }, { "input": "ПолеПароля", "path": "ПолеПароля", "passwordMode": true, "title": "Пароль" }, diff --git a/tests/skills/cases/form-compile/snapshots/groups/DataProcessors/СГруппами/Forms/Форма/Ext/Form.xml b/tests/skills/cases/form-compile/snapshots/groups/DataProcessors/СГруппами/Forms/Форма/Ext/Form.xml index 2c1dea03..cffa7b27 100644 --- a/tests/skills/cases/form-compile/snapshots/groups/DataProcessors/СГруппами/Forms/Форма/Ext/Form.xml +++ b/tests/skills/cases/form-compile/snapshots/groups/DataProcessors/СГруппами/Forms/Форма/Ext/Form.xml @@ -30,6 +30,7 @@ Use Equal None + Ctrl+G diff --git a/tests/skills/cases/form-compile/snapshots/input-fields/DataProcessors/ПоляВвода/Forms/Форма/Ext/Form.xml b/tests/skills/cases/form-compile/snapshots/input-fields/DataProcessors/ПоляВвода/Forms/Форма/Ext/Form.xml index fd815919..ad0535c2 100644 --- a/tests/skills/cases/form-compile/snapshots/input-fields/DataProcessors/ПоляВвода/Forms/Форма/Ext/Form.xml +++ b/tests/skills/cases/form-compile/snapshots/input-fields/DataProcessors/ПоляВвода/Forms/Форма/Ext/Form.xml @@ -24,9 +24,11 @@ ShowBottom + false EnterOnInput false false + Ctrl+Shift+O ru