From e048fcff708b87e76eda47c8dc4c9cc512bc6161 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 16 Aug 2026 12:30:19 +0300 Subject: [PATCH] =?UTF-8?q?fix(mxl-compile,mxl-decompile):=20=D0=BF=D1=83?= =?UTF-8?q?=D1=81=D1=82=D0=BE=D0=B9=20=D1=82=D0=B5=D0=BA=D1=81=D1=82=20?= =?UTF-8?q?=D1=8F=D1=87=D0=B5=D0=B9=D0=BA=D0=B8=20=D0=B8=20=D1=80=D0=B0?= =?UTF-8?q?=D1=81=D1=85=D0=BE=D0=B6=D0=B4=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=80=D1=82=D0=BE=D0=B2=20=D0=BD=D0=B0=20=D0=BD=D1=91?= =?UTF-8?q?=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Пустой текст платформа хранит только самозакрывающимся тегом: в корпусе ERP таких 1 224 460, а из 780 934 непустых блоков нет ни одного, где пусты все языки. Компилятор же на `text: ""` писал блок с пустым элементом языка — запись, которой в выгрузках не существует. Теперь любая пустая форма (строка, пустой объект, все языки пустыми) даёт один и тот же тег, а декомпилятор возвращает её канонической пустой строкой. Отсюда же росло расхождение портов. Свёртка текста на пустой карте языков возвращала null, если ДРУГОГО текста в макете нет вовсе: набор языков документа при этом пуст. Дальше py писал пропуск колонки, а ps1 наступал на разворачивание одноэлементного массива — список из одного $null превращался в $null, и строка целиком уходила в пустые. Проверено на 12 корпусных макетах: 1411 пустых текстов из 1411 вернулись на место, JSON портов совпадает. --- .../mxl-compile/scripts/mxl-compile.ps1 | 12 +-- .../skills/mxl-compile/scripts/mxl-compile.py | 10 +-- .../mxl-decompile/scripts/mxl-decompile.ps1 | 12 ++- .../mxl-decompile/scripts/mxl-decompile.py | 7 +- .../cases/mxl-compile/empty-cell-text.json | 16 ++++ .../snapshots/empty-cell-text/Template.xml | 75 +++++++++++++++++++ .../mxl-decompile/roundtrip-empty-text.json | 18 +++++ .../roundtrip-empty-text/Template.xml | 49 ++++++++++++ .../snapshots/roundtrip-empty-text/back.json | 1 + 9 files changed, 186 insertions(+), 14 deletions(-) create mode 100644 tests/skills/cases/mxl-compile/empty-cell-text.json create mode 100644 tests/skills/cases/mxl-compile/snapshots/empty-cell-text/Template.xml create mode 100644 tests/skills/cases/mxl-decompile/roundtrip-empty-text.json create mode 100644 tests/skills/cases/mxl-decompile/snapshots/roundtrip-empty-text/Template.xml create mode 100644 tests/skills/cases/mxl-decompile/snapshots/roundtrip-empty-text/back.json diff --git a/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 b/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 index 8f3d45b5..2760ec11 100644 --- a/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 +++ b/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 @@ -1,4 +1,4 @@ -# mxl-compile v1.50 — Compile 1C spreadsheet from JSON +# mxl-compile v1.51 — Compile 1C spreadsheet from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -1191,10 +1191,12 @@ function Emit-CellText { } else { foreach ($l in $textLanguages) { $pairs += @{ Lang = $l; Text = "$value" } } } - # Пустой объект — отдельное состояние: тег текста есть, языков в нём нет. Платформа - # пишет его самозакрывающимся. Для авторинга бесполезно (визуально это тот же пустой - # текст, что и ""), поэтому в описании DSL записи нет — она нужна раундтрипу. - if ($pairs.Count -eq 0) { + # Пустой текст платформа хранит ТОЛЬКО самозакрывающимся тегом: в корпусе ERP таких + # 1 224 460, а из 780 934 непустых блоков ни одного со всеми пустыми языками нет. + # Поэтому и пустая строка, и пустой объект дают одну и ту же запись. + $allEmpty = $true + foreach ($pr in $pairs) { if ("$($pr.Text)" -ne '') { $allEmpty = $false; break } } + if ($pairs.Count -eq 0 -or $allEmpty) { X "`t`t`t`t`t" return } diff --git a/.claude/skills/mxl-compile/scripts/mxl-compile.py b/.claude/skills/mxl-compile/scripts/mxl-compile.py index 6d3ec4a4..1f7dcaf1 100644 --- a/.claude/skills/mxl-compile/scripts/mxl-compile.py +++ b/.claude/skills/mxl-compile/scripts/mxl-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# mxl-compile v1.50 — Compile 1C spreadsheet from JSON +# mxl-compile v1.51 — Compile 1C spreadsheet from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import hashlib @@ -1210,10 +1210,10 @@ def main(): pairs = [(str(k), str(v)) for k, v in value.items()] else: pairs = [(lang, str(value)) for lang in text_languages] - # Пустой объект — отдельное состояние: тег текста есть, языков в нём нет. Платформа - # пишет его самозакрывающимся. Для авторинга бесполезно (визуально это тот же пустой - # текст, что и ""), поэтому в описании DSL записи нет — она нужна раундтрипу. - if not pairs: + # Пустой текст платформа хранит ТОЛЬКО самозакрывающимся тегом: в корпусе ERP таких + # 1 224 460, а из 780 934 непустых блоков ни одного со всеми пустыми языками нет. + # Поэтому и пустая строка, и пустой объект дают одну и ту же запись. + if not pairs or all(content == '' for _, content in pairs): lines.append('\t\t\t\t\t') return lines.append('\t\t\t\t\t') diff --git a/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 b/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 index 9775c3f6..e1739819 100644 --- a/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 +++ b/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 @@ -1,4 +1,4 @@ -# mxl-decompile v1.29 — Decompile 1C spreadsheet to JSON +# mxl-decompile v1.30 — Decompile 1C spreadsheet to JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -723,6 +723,10 @@ $textLanguages = @($docLangs.Keys) function Get-DslText { param($byLang) if ($byLang -isnot [System.Collections.IDictionary]) { return $byLang } + # Пустой текст () записывается в DSL пустой строкой — той же формой, что + # авторская. Без этой ветки макет, где ДРУГОГО текста нет вовсе, отдавал $null, + # и ячейка с пустым текстом теряла содержимое. + if ($byLang.Count -eq 0) { return '' } if ($byLang.Count -ne $textLanguages.Count) { return $byLang } $common = $null foreach ($l in $textLanguages) { @@ -1219,8 +1223,10 @@ function ConvertTo-PositionalCells { # Позиционная форма ценна компактностью: если она длиннее объектной, смысла в ней нет. $a = Try-InlineJson $out $b = Try-InlineJson $cells - if ($null -ne $a -and $null -ne $b -and $a.Length -gt $b.Length) { return $cells } - return $out + if ($null -ne $a -and $null -ne $b -and $a.Length -gt $b.Length) { return ,$cells } + # Запятая обязательна: без неё список из одной ячейки разворачивается в саму ячейку, + # а список из одного $null — в $null, и строка целиком уходила в пустые. + return ,$out } # --- 12. Build areas --- diff --git a/.claude/skills/mxl-decompile/scripts/mxl-decompile.py b/.claude/skills/mxl-decompile/scripts/mxl-decompile.py index c9819005..86e21ab4 100644 --- a/.claude/skills/mxl-decompile/scripts/mxl-decompile.py +++ b/.claude/skills/mxl-decompile/scripts/mxl-decompile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# mxl-decompile v1.29 — Decompile 1C spreadsheet to JSON +# mxl-decompile v1.30 — Decompile 1C spreadsheet to JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -849,6 +849,11 @@ def main(): def get_dsl_text(by_lang): if not isinstance(by_lang, dict): return by_lang + # Пустой текст () записывается в DSL пустой строкой — той же формой, что + # авторская. Без этой ветки макет, где ДРУГОГО текста нет вовсе, отдавал null, + # и ячейка с пустым текстом теряла содержимое. + if not by_lang: + return '' if len(by_lang) != len(text_languages): return by_lang common = None diff --git a/tests/skills/cases/mxl-compile/empty-cell-text.json b/tests/skills/cases/mxl-compile/empty-cell-text.json new file mode 100644 index 00000000..60830f4d --- /dev/null +++ b/tests/skills/cases/mxl-compile/empty-cell-text.json @@ -0,0 +1,16 @@ +{ + "name": "Пустой текст ячейки: строка, объект и все языки пустые", + "input": { + "columns": 2, + "textLanguages": ["ru", "en"], + "areas": [{ + "rows": [ + { "cells": [{ "col": 1, "text": "" }] }, + { "cells": [{ "col": 1, "text": {} }] }, + { "cells": [{ "col": 1, "text": { "ru": "", "en": "" } }] }, + { "cells": [{ "col": 1, "text": { "ru": "А", "en": "" } }] } + ] + }] + }, + "params": { "outputPath": "Template.xml" } +} diff --git a/tests/skills/cases/mxl-compile/snapshots/empty-cell-text/Template.xml b/tests/skills/cases/mxl-compile/snapshots/empty-cell-text/Template.xml new file mode 100644 index 00000000..74a37483 --- /dev/null +++ b/tests/skills/cases/mxl-compile/snapshots/empty-cell-text/Template.xml @@ -0,0 +1,75 @@ + + + + ru + ru + + ru + Русский + Русский + + + + 2 + + + 0 + + + + 0 + + + + + + + 1 + + + + 0 + + + + + + + 2 + + + + 0 + + + + + + + 3 + + + + 0 + + + ru + А + + + en + + + + + + + + true + 1 + 4 + 4 + + 10 + + \ No newline at end of file diff --git a/tests/skills/cases/mxl-decompile/roundtrip-empty-text.json b/tests/skills/cases/mxl-decompile/roundtrip-empty-text.json new file mode 100644 index 00000000..442ddfd8 --- /dev/null +++ b/tests/skills/cases/mxl-decompile/roundtrip-empty-text.json @@ -0,0 +1,18 @@ +{ + "name": "Roundtrip — пустой текст ячейки возвращается как \"\"", + "preRun": [ + { "script": "mxl-compile/scripts/mxl-compile", + "input": { + "columns": 2, + "areas": [{ "rows": [ + { "cells": [{ "col": 1, "text": "" }] }, + { "cells": [{ "col": 1, "text": "Текст" }] } + ]}] + }, + "args": { "-JsonPath": "{inputFile}", "-OutputPath": "Template.xml" }, + "cwd": "{workDir}" } + ], + "params": { "templatePath": "Template.xml" }, + "args_extra": ["-OutputPath", "{workDir}/back.json"], + "expect": { "files": ["back.json"], "stdoutContains": "[OK] Decompiled:" } +} diff --git a/tests/skills/cases/mxl-decompile/snapshots/roundtrip-empty-text/Template.xml b/tests/skills/cases/mxl-decompile/snapshots/roundtrip-empty-text/Template.xml new file mode 100644 index 00000000..8f0a78c2 --- /dev/null +++ b/tests/skills/cases/mxl-decompile/snapshots/roundtrip-empty-text/Template.xml @@ -0,0 +1,49 @@ + + + + ru + ru + + ru + Русский + Русский + + + + 2 + + + 0 + + + + 0 + + + + + + + 1 + + + + 0 + + + ru + Текст + + + + + + + true + 1 + 2 + 2 + + 10 + + \ No newline at end of file diff --git a/tests/skills/cases/mxl-decompile/snapshots/roundtrip-empty-text/back.json b/tests/skills/cases/mxl-decompile/snapshots/roundtrip-empty-text/back.json new file mode 100644 index 00000000..35c04f31 --- /dev/null +++ b/tests/skills/cases/mxl-decompile/snapshots/roundtrip-empty-text/back.json @@ -0,0 +1 @@ +{ "columns": 2, "defaultWidth": 10, "fonts": {}, "styles": {}, "areas": [{ "rows": [[""], ["Текст"]] }] } \ No newline at end of file