From 2d326c99a5bb117a61a7ed6ab944b064908aa547 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 13 Jun 2026 14:41:17 +0300 Subject: [PATCH] =?UTF-8?q?feat(form-decompile,form-compile):=20choiceList?= =?UTF-8?q?=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=E2=80=94?= =?UTF-8?q?=20DesignTimeRef=20=D0=BF=D0=BE=20GUID=20+=20nil?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Значение элемента (InputField/RadioButtonField): (1) GUID.GUID — ссылка по метаданным-GUID (raw, не по имени) эмитилась как xs:string: декомпилятор исключал DesignTimeRef из valueType (расчёт на авто-детект компилятора), но Normalize-ChoiceValue детектит только named-ссылки (Enum.X.Y), GUID.GUID → xs:string. Фикс: декомпилятор сохраняет valueType="xr:DesignTimeRef" при значении-GUID (по префиксу GUID); named-ссылки по-прежнему авто-детектятся. (2) — nil-значение варианта эмитилось как typed-empty xs:string (Convert-TypedValue пустого nil-узла → ""). Фикс: декомпилятор ставит valueType="nil", компилятор эмитит . Зеркало py. Выборка 15 форм (ИндексацияЗаработка/ФормаДокумента, РассылкиОтчетов, …): match 13→15 целевых (остаток 2 формы — отдельный кластер dcsset:left булев-литерал). ps1==py байт-в-байт. Регресс 43/43. Spec обновлён (choiceList valueType nil/DesignTimeRef). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../form-compile/scripts/form-compile.ps1 | 7 +++--- .../form-compile/scripts/form-compile.py | 13 ++++++----- .../form-decompile/scripts/form-decompile.ps1 | 22 +++++++++++++------ docs/form-dsl-spec.md | 2 +- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1 index 0c43f1cd..8df7df3b 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.157 — Compile 1C managed form from JSON or object metadata +# form-compile v1.158 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$JsonPath, @@ -4139,7 +4139,8 @@ function Emit-ChoiceList { if ($item.Contains("valueType")) { $vtRaw = "$($item["valueType"])" } } elseif ($item.PSObject.Properties["valueType"]) { $vtRaw = "$($item.valueType)" } - if ($vtRaw) { $norm = @{ XsiType = $vtRaw; Text = "$valRaw" } } + if ($vtRaw -eq 'nil') { $norm = @{ XsiType = $null; Text = $null; Nil = $true } } + elseif ($vtRaw) { $norm = @{ XsiType = $vtRaw; Text = "$valRaw" } } else { $norm = Normalize-ChoiceValue -value $valRaw } # авто-вывод presentation, если не задан @@ -4158,7 +4159,7 @@ function Emit-ChoiceList { X "$valIndent0" X "$valIndent" Emit-ChoicePresentation -pres $presRaw -indent "$valIndent`t" - X "$valIndent`t$(Get-ChoiceValueTag $norm)" + X "$valIndent`t$(if ($norm.Nil) { '' } else { Get-ChoiceValueTag $norm })" X "$valIndent" X "$itemIndent" } diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py index 5cd24f84..6a5033d2 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.157 — Compile 1C managed form from JSON or object metadata +# form-compile v1.158 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import copy @@ -2327,17 +2327,19 @@ def emit_choice_list(lines, el, indent): # valueType: явный xsi:type значения (системное перечисление ent:*, иной не-примитив) — # переопределяет авто-детект (normalize_choice_value вывела бы xs:string). vt_raw = item.get('valueType') - if vt_raw: + if vt_raw == 'nil': + norm = {'xsi_type': None, 'text': None, 'nil': True} + elif vt_raw: norm = {'xsi_type': str(vt_raw), 'text': '' if val_raw is None else str(val_raw)} else: norm = normalize_choice_value(val_raw) if not has_pres: - if norm['xsi_type'] == 'xr:DesignTimeRef': + if norm.get('xsi_type') == 'xr:DesignTimeRef': tail = norm['text'].split('.')[-1] pres_raw = title_from_name(tail) else: - pres_raw = norm['text'] + pres_raw = norm.get('text') lines.append(f'{item_indent}') val_indent = f'{item_indent}\t' @@ -2345,7 +2347,8 @@ def emit_choice_list(lines, el, indent): lines.append(f'{val_indent}0') lines.append(f'{val_indent}') emit_choice_presentation(lines, pres_raw, f'{val_indent}\t') - lines.append(f'{val_indent}\t{choice_value_tag(norm)}') + val_tag = '' if norm.get('nil') else choice_value_tag(norm) + lines.append(f'{val_indent}\t{val_tag}') lines.append(f'{val_indent}') lines.append(f'{item_indent}') lines.append(f'{indent}') diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1 index 96f59f90..b3df467c 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.132 — Decompile 1C managed Form.xml to JSON DSL (draft) +# form-decompile v0.133 — Decompile 1C managed Form.xml to JSON DSL (draft) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью. param( @@ -1677,12 +1677,20 @@ function Decompile-ChoiceList { $presNode = $it.SelectSingleNode("xr:Value/lf:Presentation", $ns) $ci = [ordered]@{} if ($valNode) { - $xsiType = $valNode.GetAttribute("type", $NS_XSI) - $ci['value'] = Convert-TypedValue $valNode.InnerText $xsiType - # Системное перечисление (ent:*) / иной не-примитивный, не-DesignTimeRef тип → сохраняем - # valueType (Normalize-ChoiceValue в компиляторе вывела бы xs:string и потеряла тип). - if ($xsiType -and $xsiType -notmatch '^xs:(string|decimal|boolean|dateTime)$' -and $xsiType -ne 'xr:DesignTimeRef') { - $ci['valueType'] = $xsiType + if ($valNode.GetAttribute("nil", $NS_XSI) -eq 'true') { + # nil-значение элемента choiceList — компилятор эмитит + # (иначе Convert-TypedValue вернул бы "" → typed-empty xs:string). + $ci['valueType'] = 'nil' + } else { + $xsiType = $valNode.GetAttribute("type", $NS_XSI) + $ci['value'] = Convert-TypedValue $valNode.InnerText $xsiType + # Системное перечисление (ent:*) / иной не-примитивный тип → сохраняем valueType + # (Normalize-ChoiceValue вывела бы xs:string). DesignTimeRef обычно авто-детектится + # компилятором по named-ref (Enum.X.Y), НО raw-ссылка по GUID (GUID.GUID) — нет → сохраняем. + if ($xsiType -and $xsiType -notmatch '^xs:(string|decimal|boolean|dateTime)$' -and ` + ($xsiType -ne 'xr:DesignTimeRef' -or "$($valNode.InnerText)" -match '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-')) { + $ci['valueType'] = $xsiType + } } } # Presentation: непустой → текст/мультиязык; пустой → "" — суппресс-маркер, diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md index fe0f63e2..3f05232d 100644 --- a/docs/form-dsl-spec.md +++ b/docs/form-dsl-spec.md @@ -505,7 +505,7 @@ companion-панели с собственным контентом. Оба не | Свойство | Тип | Описание | |----------|-----|----------| | `value` | string/number/bool | Значение варианта. Для перечисления — `"Enum.ИмяТипа.EnumValue.ИмяЗначения"` (xsi:type автоматически: `xr:DesignTimeRef` / `xs:string` / `xs:decimal` / `xs:boolean`) | -| `valueType` | string | Явный xsi:type значения, переопределяет авто-детект. Нужен для **системных перечислений** (`ent:` namespace: `ent:AccountType`=ВидСчёта, `ent:AccumulationRecordType`, `ent:HorizontalAlignment`, … — см. «Системные перечисления» в палитре типов) и иных не-примитивных типов. Напр. `{ "value": "Active", "valueType": "ent:AccountType", "presentation": "Активный" }` | +| `valueType` | string | Явный xsi:type значения, переопределяет авто-детект. Нужен для **системных перечислений** (`ent:` namespace: `ent:AccountType`=ВидСчёта, `ent:AccumulationRecordType`, `ent:HorizontalAlignment`, … — см. «Системные перечисления» в палитре типов) и иных не-примитивных типов. Напр. `{ "value": "Active", "valueType": "ent:AccountType", "presentation": "Активный" }`. Спец-маркеры (раундтрип): **`"nil"`** → `` (пустое значение варианта без типа); **`"xr:DesignTimeRef"`** при значении-GUID (`GUID.GUID` — ссылка по метаданным-GUID, не по имени; named-ссылки `Enum.X.Y` авто-детектятся без ключа) | | `presentation` | string или object | Текст рядом с переключателем. Строка → ru; объект `{ru, en, ...}` → мультиязык. Если не задано — выводится из имени значения | #### label — LabelDecoration