diff --git a/.claude/skills/mxl-compile/reference/dsl-spec.md b/.claude/skills/mxl-compile/reference/dsl-spec.md index 20405775..bbdcd532 100644 --- a/.claude/skills/mxl-compile/reference/dsl-spec.md +++ b/.claude/skills/mxl-compile/reference/dsl-spec.md @@ -144,12 +144,16 @@ | Элемент | Значение | |---------|----------| | `"текст"` | Статический текст (`text`) | +| `{ "ru": "…", "en": "…" }` | Тот же текст на нескольких языках | | `"{Имя}"` | Параметр (`param`) | | `">"` | Продолжение ячейки слева — увеличивает её `span` | | `"|"` | Продолжение ячейки сверху — увеличивает её `rowspan` | | `null` | Пустая колонка: позиция занята, ячейка не создаётся | | `{ ... }` | Обычная ячейка **без** `col`; нужна для `style`, `detail`, `template` | +Объект-элемент читается как ячейка, если несёт хоть один её ключ (`span`, `rowspan`, `style`, +`param`, `detail`, `text`, `template`), и как многоязычный текст в противном случае. + ```json "rows": [ ["Вид", "Остаток", ">", "Итог"], diff --git a/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 b/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 index 00401b3a..59502281 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.36 — Compile 1C spreadsheet from JSON +# mxl-compile v1.37 — Compile 1C spreadsheet from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -845,6 +845,17 @@ function Set-CellProp { $cell | Add-Member -NotePropertyName $name -NotePropertyValue $value -Force } +# Ячейка это или многоязычный текст: у ячейки есть хоть один ключ её схемы, у ML-значения +# ключи — идентификаторы языков. Пересечений нет: в корпусе это ru, en, ru1, Русский. +function Test-CellObject { + param($el) + $cellKeys = @('col', 'span', 'rowspan', 'style', 'param', 'detail', 'text', 'template') + foreach ($p in $el.PSObject.Properties) { + if ($cellKeys -contains $p.Name) { return $true } + } + return $false +} + function Expand-ShorthandRow { param($row, [string]$areaName, [int]$rowIdx, $openByCol, [int]$maxCols) @@ -897,6 +908,12 @@ function Expand-ShorthandRow { $m = [regex]::Match($el, '^\{(.+)\}$') if ($m.Success) { Set-CellProp $cell 'param' $m.Groups[1].Value } else { Set-CellProp $cell 'text' $el } + } elseif (-not (Test-CellObject $el)) { + # Объект без единого ключа ячейки — это многоязычный ТЕКСТ: в позиционной записи + # элемент и есть значение текста, а значение текста по общей конвенции бывает + # строкой либо объектом «язык → текст». + $cell = [PSCustomObject]@{ col = $idx; span = 1 } + Set-CellProp $cell 'text' $el } else { # Объектный элемент — обычная ячейка mxl, позиция берётся из индекса. if ($el.PSObject.Properties['col']) { diff --git a/.claude/skills/mxl-compile/scripts/mxl-compile.py b/.claude/skills/mxl-compile/scripts/mxl-compile.py index 0cac8831..7ecda00a 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.36 — Compile 1C spreadsheet from JSON +# mxl-compile v1.37 — Compile 1C spreadsheet from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import hashlib @@ -884,6 +884,13 @@ def main(): # "{Имя}" — параметр. Разворачиваем в обычную строку с явными col/span/rowspan, # поэтому весь код ниже про шорткат не знает. + def is_cell_object(el): + """Ячейка это или многоязычный текст: у ячейки есть хоть один ключ её схемы, + у ML-значения ключи — идентификаторы языков. Пересечений нет: в корпусе это + ru, en, ru1, Русский.""" + cell_keys = ('col', 'span', 'rowspan', 'style', 'param', 'detail', 'text', 'template') + return any(k in el for k in cell_keys) + def expand_shorthand_row(row, area_name, row_idx, open_by_col, max_cols): cells = [] placed = {} # 1-based col -> ячейка, занимающая колонку в ЭТОЙ строке @@ -933,6 +940,11 @@ def main(): cell['param'] = m.group(1) else: cell['text'] = el + elif not is_cell_object(el): + # Объект без единого ключа ячейки — это многоязычный ТЕКСТ: в позиционной + # записи элемент и есть значение текста, а значение текста по общей конвенции + # бывает строкой либо объектом «язык → текст». + cell = CIDict({'col': idx, 'span': 1, 'text': el}) else: # Объектный элемент — обычная ячейка mxl, позиция берётся из индекса. if 'col' in el: diff --git a/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 b/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 index 2b6974d7..b6222c5e 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.15 — Decompile 1C spreadsheet to JSON +# mxl-decompile v1.16 — Decompile 1C spreadsheet to JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -631,20 +631,17 @@ function Get-ColumnStyles { return $out } -# Список признаётся позиционным по тому же правилу, что и в компиляторе: есть элемент-строка -# или пропуск. Список из одних объектов таковым не считаем — он читается как обычный. +# Список позиционный, если позиция в нём НЕ записана: ни у одного элемента нет col. +# Прежняя проверка искала элемент-строку, поэтому строка из одних многоязычных текстов +# позиционной не признавалась и оставалась объектной. function Test-PositionalList { param($cells) foreach ($el in $cells) { - if ($null -eq $el -or $el -is [string]) { return $true } + if ($el -is [System.Collections.IDictionary] -and $el.Contains('col')) { return $false } } - return $false + return $true } -# Позиционная запись списка ячеек: позиция берётся из порядка, `col` не пишется. -# Применяем, когда первая ячейка стоит в колонке 1 — иначе список начнётся с череды null -# и станет длиннее объектного. Ячейка, у которой кроме текста или параметра ничего нет, -# пишется строкой; span раскрывается маркерами ">"; всё прочее — объектным элементом без col. # Custom JSON serializer — компактный, 2-пробельный indent, массивы примитивов inline. # В отличие от ConvertTo-Json (PS5.1): # - не выравнивает ключи объекта по самому длинному @@ -771,6 +768,10 @@ function ConvertTo-CompactJson { return (Convert-StringToJsonLiteral "$obj") } +# Позиционная запись списка ячеек: позиция берётся из порядка, `col` не пишется. +# Ячейка, у которой кроме текста или параметра ничего нет, пишется значением; span +# раскрывается маркерами ">"; пропуск колонки — null; всё прочее — объектным элементом +# без col. Выбираем ту запись, которая КОРОЧЕ. function ConvertTo-PositionalCells { param($cells) # Пропуск колонки — null. Выбираем ту запись, которая КОРОЧЕ: раньше позиционная форма @@ -787,10 +788,12 @@ function ConvertTo-PositionalCells { $span = if ($c.Contains("span")) { [int]$c["span"] } else { 1 } $keys = @($c.Keys | Where-Object { $_ -notin @("col", "span") }) - $plainText = ($keys.Count -eq 1 -and $keys[0] -eq "text" -and $c["text"] -is [string]) + # Текст — единственное содержимое: пишем значением. Оно бывает строкой либо + # объектом «язык → текст», и в позиционной записи элемент И ЕСТЬ это значение. + $plainText = ($keys.Count -eq 1 -and $keys[0] -eq "text") $plainParam = ($keys.Count -eq 1 -and $keys[0] -eq "param") - if ($plainText) { $out += $c["text"] } + if ($plainText) { $out += , $c["text"] } elseif ($plainParam) { $out += "{$($c["param"])}" } else { $obj = [ordered]@{} diff --git a/.claude/skills/mxl-decompile/scripts/mxl-decompile.py b/.claude/skills/mxl-decompile/scripts/mxl-decompile.py index 4b36ac69..3db15048 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.15 — Decompile 1C spreadsheet to JSON +# mxl-decompile v1.16 — Decompile 1C spreadsheet to JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -791,7 +791,7 @@ def main(): def to_positional_cells(cells): """Позиционная запись списка ячеек: позиция берётся из порядка, `col` не пишется. - Ячейка, у которой кроме текста или параметра ничего нет, пишется строкой; span + Ячейка, у которой кроме текста или параметра ничего нет, пишется значением; span раскрывается маркерами ">"; прочее — объектным элементом без col. Пропуск колонки — null. Выбираем ту запись, которая КОРОЧЕ: раньше позиционная форма отбрасывалась, как только первая ячейка стояла не в первой колонке, хотя один-два null впереди @@ -809,7 +809,9 @@ def main(): expected += 1 span = int(c.get("span", 1) or 1) keys = [k for k in c if k not in ("col", "span")] - plain_text = len(keys) == 1 and keys[0] == "text" and isinstance(c["text"], str) + # Текст — единственное содержимое: пишем значением. Оно бывает строкой либо + # объектом «язык → текст», и в позиционной записи элемент И ЕСТЬ это значение. + plain_text = len(keys) == 1 and keys[0] == "text" plain_param = len(keys) == 1 and keys[0] == "param" if plain_text: out.append(c["text"]) @@ -1019,8 +1021,11 @@ def main(): dsl_row["cells"] = to_positional_cells(dsl_cells) # Самая короткая из применимых форм: если у строки нет своих свойств, а список ячеек # позиционный — строка пишется просто массивом, без ключа cells. + # Список позиционный, если позиция в нём НЕ записана: ни у одного элемента нет col. + # Прежняя проверка искала элемент-строку, поэтому строка из одних многоязычных + # текстов позиционной не признавалась и оставалась объектной. if (len(dsl_row) == 1 and "cells" in dsl_row - and any(el is None or isinstance(el, str) for el in dsl_row["cells"])): + and not any(isinstance(el, dict) and "col" in el for el in dsl_row["cells"])): area_rows.append(dsl_row["cells"]) else: area_rows.append(dsl_row) diff --git a/docs/mxl-dsl-spec.md b/docs/mxl-dsl-spec.md index e21198cc..91ca984e 100644 --- a/docs/mxl-dsl-spec.md +++ b/docs/mxl-dsl-spec.md @@ -144,12 +144,16 @@ | Элемент | Значение | |---------|----------| | `"текст"` | Статический текст (`text`) | +| `{ "ru": "…", "en": "…" }` | Тот же текст на нескольких языках | | `"{Имя}"` | Параметр (`param`) | | `">"` | Продолжение ячейки слева — увеличивает её `span` | | `"|"` | Продолжение ячейки сверху — увеличивает её `rowspan` | | `null` | Пустая колонка: позиция занята, ячейка не создаётся | | `{ ... }` | Обычная ячейка **без** `col`; нужна для `style`, `detail`, `template` | +Объект-элемент читается как ячейка, если несёт хоть один её ключ (`span`, `rowspan`, `style`, +`param`, `detail`, `text`, `template`), и как многоязычный текст в противном случае. + ```json "rows": [ ["Вид", "Остаток", ">", "Итог"],