From e3ae9c27d17ab5ea548b86884ddc8efaec05a93a Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 13 Jun 2026 13:37:39 +0300 Subject: [PATCH] =?UTF-8?q?feat(form-decompile,form-compile):=20=D0=BF?= =?UTF-8?q?=D1=83=D1=81=D1=82=D0=BE=D0=B9/whitespace=20right=20=D1=84?= =?UTF-8?q?=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D0=B0=20+=20GetInvisibleFieldPre?= =?UTF-8?q?sentations=20(=D0=BA=D0=BB=D0=B0=D1=81=D1=82=D0=B5=D1=80=20Attr?= =?UTF-8?q?ibute>right)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (1) Пустой ≠ отсутствие : декомпилятор схлопывал оба в shorthand-маркер `_`, а компилятор для shorthand `_` не эмитит right вовсе → пустой right терялся (Get-FilterValueWithType маппит наличие пустого/nil right в '_', отсутствие → $null — РАЗЛИЧИМЫ). Фикс (декомпилятор): при value='_' с реально присутствующим форсим объектную форму {value:"_"} — компилятор эмитит self-closing right (ветка `_` уже была). Заодно whitespace-/пробельные значения, рвущие shorthand-парсинг (split по пробелам), уходят в объектную форму. (2) Whitespace-only (9 пробелов): PreserveWhitespace=false стрипал в '' → '_' → self-closing. Восстанавливаем реальные пробелы из WS-дока (Resolve-WS, как у whitespace-заголовков) → объектная форма value=" ". (3) GetInvisibleFieldPresentations — Settings-скаляр дин-списка (после MainTable; дефолт true, корпус 20/20 = false → эмит отклонения). Захват/эмит факт. значения, зеркало py. Выборка 14 форм (ДоговорыКонтрагентов, ЕдиницыГенерирующие×2, РаботаСНоменклатурой, ПравилаИнтеграции, … acc+erp): match 0→14, TOTAL→0. ps1==py байт-в-байт. Регресс 43/43. Spec обновлён (getInvisibleFieldPresentations). (1)/(2) — декомпилятор-only. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../form-compile/scripts/form-compile.ps1 | 4 +++- .../form-compile/scripts/form-compile.py | 5 +++- .../form-decompile/scripts/form-decompile.ps1 | 24 +++++++++++++++++-- docs/form-dsl-spec.md | 1 + 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1 index fb35c14b..6a3b4485 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.153 — Compile 1C managed form from JSON or object metadata +# form-compile v1.154 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$JsonPath, @@ -5722,6 +5722,8 @@ function Emit-Attributes { if ($st.keyType) { X "$si$(Esc-Xml "$($st.keyType)")" } if ($st.keyFields) { foreach ($kf in @($st.keyFields)) { X "$si$(Esc-Xml "$kf")" } } if ($st.mainTable) { X "$si$(Normalize-MetaTypeRef "$($st.mainTable)")" } + # GetInvisibleFieldPresentations — после MainTable (дефолт true; эмитим только при заданном ключе = отклонении false). + if ($null -ne $st.getInvisibleFieldPresentations) { X "$si$(if ($st.getInvisibleFieldPresentations){'true'}else{'false'})" } # AutoSaveUserSettings — после MainTable (дефолт true; эмитим только при заданном ключе = отклонении). if ($null -ne $st.autoSaveUserSettings) { X "$si$(if ($st.autoSaveUserSettings){'true'}else{'false'})" } # ListSettings: filter/order/conditionalAppearance (skd-грамматика) + каноничные блок-GUID. diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py index 80682fb2..0afced2e 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.153 — Compile 1C managed form from JSON or object metadata +# form-compile v1.154 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import copy @@ -5480,6 +5480,9 @@ def emit_attributes(lines, attrs, indent, conditional_appearance=None): lines.append(f'{si}{esc_xml(str(kf))}') if s.get('mainTable'): lines.append(f'{si}{normalize_meta_type_ref(str(s["mainTable"]))}') + # GetInvisibleFieldPresentations — после MainTable (дефолт true; эмитим только при заданном ключе = отклонении false). + if s.get('getInvisibleFieldPresentations') is not None: + lines.append(f'{si}{"true" if s["getInvisibleFieldPresentations"] else "false"}') # AutoSaveUserSettings — после MainTable (дефолт true; эмитим только при заданном ключе = отклонении). if s.get('autoSaveUserSettings') is not None: lines.append(f'{si}{"true" if s["autoSaveUserSettings"] else "false"}') diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1 index 52eb0854..b3e33116 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.128 — Decompile 1C managed Form.xml to JSON DSL (draft) +# form-decompile v0.129 — Decompile 1C managed Form.xml to JSON DSL (draft) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью. param( @@ -732,7 +732,25 @@ function Build-FilterItem { $noValueOps = @('filled','notFilled') - if ($userPresNode -or $valueIsArrayFlag -or $valueTypeAttr -or $fiPres) { + # Пустой xs:string right () ≠ отсутствие : + # Get-FilterValueWithType маппит наличие пустого/nil right в value='_' (отсутствие → $null). + # Shorthand `_` неоднозначен (схлопывает оба, компилятор → нет right). Пробельные значения рвут + # shorthand-парсинг (split по пробелам). → форсим объектную форму (value='_' → self-closing; + # пробелы — как есть в ). + $valNeedsObj = $false + if ($rightNodes.Count -eq 1 -and -not $valueIsArrayFlag -and $op -notin $noValueOps) { + if ("$value" -eq '_') { + # Truly-empty ИЛИ whitespace-only + # (PreserveWhitespace=false стрипнул пробелы в '' → Get-FilterValueWithType вернул '_'). + # Восстанавливаем реальные пробелы из WS-дока (как у whitespace-заголовков). + $ws = Resolve-WS $rightNodes[0] + if ($ws -and $ws.Length -gt 0 -and $ws.Trim() -eq '') { $value = $ws } + $valNeedsObj = $true + } + elseif ($null -ne $value -and "$value" -match '\s') { $valNeedsObj = $true } + } + + if ($userPresNode -or $valueIsArrayFlag -or $valueTypeAttr -or $fiPres -or $valNeedsObj) { $obj = [ordered]@{ field = $field; op = $op } if ($op -notin $noValueOps -and $null -ne $value) { if ($valueIsArrayFlag) { @@ -2693,6 +2711,8 @@ if ($attrsNode) { # AutoFillAvailableFields — дефолт true, платформа эмитит только отклонение (false). Захват «как есть». $afaf = Get-Child $setNode 'AutoFillAvailableFields'; if ($null -ne $afaf) { $so['autoFillAvailableFields'] = ($afaf -eq 'true') } $mt = Get-Child $setNode 'MainTable'; if ($mt) { $so['mainTable'] = $mt } + # GetInvisibleFieldPresentations — дефолт true, платформа эмитит только отклонение (false, корпус 20/20). Факт. значение. + $gifp = Get-Child $setNode 'GetInvisibleFieldPresentations'; if ($null -ne $gifp) { $so['getInvisibleFieldPresentations'] = ($gifp -eq 'true') } # Ключ набора (query-based список): KeyType (RowNumber/FieldValue/RowKey) + KeyField* (0+). $kt = Get-Child $setNode 'KeyType'; if ($kt) { $so['keyType'] = $kt } $kfNodes = @($setNode.SelectNodes("lf:KeyField", $ns) | ForEach-Object { $_.InnerText }) diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md index 3ec416fe..c2f23c9a 100644 --- a/docs/form-dsl-spec.md +++ b/docs/form-dsl-spec.md @@ -943,6 +943,7 @@ Forgiving-синонимы типа: XML-имя (`SpreadSheetDocumentField`) и | `keyType` | string | Тип ключа набора запросного списка (без `mainTable`): `FieldValue` / `RowKey` / `RowNumber` | | `keyFields` | array | Поля ключа набора (``, 0+) — для запросного списка без `mainTable`. Эмитятся после параметров | | `autoSaveUserSettings` | bool | Авто-сохранение пользовательских настроек дин-списка (``, после `MainTable`). **Умолчание `true`** — указывать только для отключения (`false`) | +| `getInvisibleFieldPresentations` | bool | Получать представления невидимых полей (``, после `MainTable`). **Умолчание `true`** — указывать только для отключения (`false`) | | `query` | string | Текст запроса (`ManualQuery=true`). Поддерживает `@file.sql` (путь относительно JSON) | | `dynamicDataRead` | bool | Динамическое считывание. **Умолчание `true`** — указывать только для отключения (`false`) | | `autoFillAvailableFields` | bool | Автозаполнение доступных полей (``). **Умолчание `true`** — указывать только для отключения (`false`; тогда поля берутся из явного запроса, не авто). Эмитится первым в `` |