mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-17 16:50:30 +03:00
fix(mxl-decompile): свести порты — пробелы, порядок и свойства значения ячейки
Расхождение JSON между портами было 26 макетов из 40, стало 5. Расхождение собранного XML — 20 из 40, стало 7. Причин оказалось четыре, и три из них дефекты, а не просто разнобой. 1. ps1 грузил XML с PreserveWhitespace = $false, и текст ячейки из одних пробелов схлопывался в пустой. Молчаливая потеря данных в каноничном порте, 25 макетов пилота. 2. Стили обнаруживались обходом хэш-таблицы строк, а её порядок в PowerShell НЕ определён. Платформа же кладёт записи палитры в порядке документа, так что порядок обхода — часть верности вывода, а не деталь. Обход теперь по возрастанию номера строки. ([ordered] тут не годится: с целочисленными ключами он индексируется по позиции, а не по ключу.) 3. Именованные области сортировались нестабильным Sort-Object (-Stable появился только в PowerShell 6.2), поэтому области с одинаковыми границами получали произвольный порядок. Добавлен явный ключ исходного порядка — в оба порта, чтобы совпадение было по построению, а не по совпадению. 4. containsValue / valueType / controlType протекали в styles сквозным пробросом неизвестных тегов. Это свойства ЗНАЧЕНИЯ ячейки, а не оформления, и компилятор таких ключей не знает — в DSL они были чистым шумом. Заодно вложенный элемент больше не читается как скаляр: ps1 брал InnerText и получал склейку поддерева, py брал .text и получал пустоту. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e46db618e3
commit
c11f4837fd
@@ -1,4 +1,4 @@
|
|||||||
# mxl-decompile v1.16 — Decompile 1C spreadsheet to JSON
|
# mxl-decompile v1.17 — Decompile 1C spreadsheet to JSON
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory)]
|
||||||
@@ -19,7 +19,9 @@ if (-not (Test-Path $TemplatePath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$xmlDoc = New-Object System.Xml.XmlDocument
|
$xmlDoc = New-Object System.Xml.XmlDocument
|
||||||
$xmlDoc.PreserveWhitespace = $false
|
# PreserveWhitespace = $false схлопывает узел из одних пробелов в пустой, и текст ячейки
|
||||||
|
# вида " " терялся молча (25 макетов пилота). Значащие пробелы в тексте надписи есть.
|
||||||
|
$xmlDoc.PreserveWhitespace = $true
|
||||||
$xmlDoc.Load((Resolve-Path $TemplatePath).Path)
|
$xmlDoc.Load((Resolve-Path $TemplatePath).Path)
|
||||||
|
|
||||||
$root = $xmlDoc.DocumentElement
|
$root = $xmlDoc.DocumentElement
|
||||||
@@ -139,6 +141,9 @@ foreach ($fmtNode in $root.SelectNodes("d:format", $ns)) {
|
|||||||
if ($item -and $item.InnerText) { $fmt.Props[$tag] = $item.InnerText }
|
if ($item -and $item.InnerText) { $fmt.Props[$tag] = $item.InnerText }
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
# Вложенный элемент скаляром не является: InnerText склеил бы всё поддерево вместе
|
||||||
|
# с переводами строк. py-порт брал .text и получал пустоту — отсюда расхождение портов.
|
||||||
|
if ($child.SelectNodes('*').Count -gt 0) { continue }
|
||||||
$val = $child.InnerText
|
$val = $child.InnerText
|
||||||
if ([string]::IsNullOrWhiteSpace($val)) { continue }
|
if ([string]::IsNullOrWhiteSpace($val)) { continue }
|
||||||
if ($formatKinds[$tag] -ceq 'color') { $val = ConvertTo-DslColor $child $val }
|
if ($formatKinds[$tag] -ceq 'color') { $val = ConvertTo-DslColor $child $val }
|
||||||
@@ -246,6 +251,10 @@ foreach ($niNode in $root.SelectNodes("d:namedItem", $ns)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$namedAreas += @{
|
$namedAreas += @{
|
||||||
|
# Порядок в исходнике: Sort-Object в PowerShell 5.1 НЕстабилен (-Stable появился
|
||||||
|
# в 6.2), поэтому области с одинаковыми границами получали произвольный порядок,
|
||||||
|
# и вывод расходился с py-портом от запуска к запуску.
|
||||||
|
Ord = $namedAreas.Count
|
||||||
Name = $niNode.SelectSingleNode("d:name", $ns).InnerText
|
Name = $niNode.SelectSingleNode("d:name", $ns).InnerText
|
||||||
Type = $areaNode.SelectSingleNode("d:type", $ns).InnerText
|
Type = $areaNode.SelectSingleNode("d:type", $ns).InnerText
|
||||||
BeginRow = & $getCoord 'beginRow'
|
BeginRow = & $getCoord 'beginRow'
|
||||||
@@ -368,7 +377,11 @@ function Get-DslText {
|
|||||||
|
|
||||||
# Свойства формата, которые стилем НЕ являются: ширина принадлежит колонке, высота —
|
# Свойства формата, которые стилем НЕ являются: ширина принадлежит колонке, высота —
|
||||||
# строке, вид заполнения выводится из того, чем задано содержимое ячейки.
|
# строке, вид заполнения выводится из того, чем задано содержимое ячейки.
|
||||||
$nonStyleTags = @('width', 'height', 'fillType', 'font')
|
# containsValue/valueType/controlType — свойства ЗНАЧЕНИЯ ячейки, а не оформления: стиль
|
||||||
|
# общий на многие ячейки, а они индивидуальны. Компилятор таких ключей не знает, так что
|
||||||
|
# в DSL они были чистым шумом.
|
||||||
|
$nonStyleTags = @('width', 'height', 'fillType', 'font',
|
||||||
|
'containsValue', 'valueType', 'controlType')
|
||||||
$borderTags = @('border', 'leftBorder', 'topBorder', 'rightBorder', 'bottomBorder')
|
$borderTags = @('border', 'leftBorder', 'topBorder', 'rightBorder', 'bottomBorder')
|
||||||
|
|
||||||
function ConvertTo-DslLine {
|
function ConvertTo-DslLine {
|
||||||
@@ -492,7 +505,11 @@ for ($i = 1; $i -lt $rawFonts.Count; $i++) {
|
|||||||
$styleKeys = [ordered]@{}
|
$styleKeys = [ordered]@{}
|
||||||
$formatToStyleKey = @{}
|
$formatToStyleKey = @{}
|
||||||
|
|
||||||
foreach ($r in $rowData.Values) {
|
# Обход по ВОЗРАСТАНИЮ номера строки: у обычной хэш-таблицы порядок в PowerShell не
|
||||||
|
# определён, и стили обнаруживались в порядке хэшей. Платформа же кладёт записи палитры
|
||||||
|
# в порядке документа, поэтому порядок обхода здесь — часть верности вывода.
|
||||||
|
# ([ordered] с целочисленными ключами не годится: он индексируется по позиции, а не по ключу.)
|
||||||
|
foreach ($r in ($rowData.Keys | Sort-Object { [int]$_ } | ForEach-Object { $rowData[$_] })) {
|
||||||
foreach ($cell in $r.Cells) {
|
foreach ($cell in $r.Cells) {
|
||||||
$fmt = Get-Format $cell.FormatIdx
|
$fmt = Get-Format $cell.FormatIdx
|
||||||
if (-not $fmt) { continue }
|
if (-not $fmt) { continue }
|
||||||
@@ -520,7 +537,7 @@ function Get-RowStyleFmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Строка — тоже владелец формата: её оформление становится именованным стилем.
|
# Строка — тоже владелец формата: её оформление становится именованным стилем.
|
||||||
foreach ($rd in $rowData.Values) {
|
foreach ($rd in ($rowData.Keys | Sort-Object { [int]$_ } | ForEach-Object { $rowData[$_] })) {
|
||||||
if ($rd.FormatIdx -le 0) { continue }
|
if ($rd.FormatIdx -le 0) { continue }
|
||||||
$rf = Get-RowStyleFmt (Get-Format $rd.FormatIdx)
|
$rf = Get-RowStyleFmt (Get-Format $rd.FormatIdx)
|
||||||
if (-not $rf) { continue }
|
if (-not $rf) { continue }
|
||||||
@@ -844,7 +861,7 @@ function Test-UniformColumnSet {
|
|||||||
$blockAreas = @()
|
$blockAreas = @()
|
||||||
$overlayAreas = @()
|
$overlayAreas = @()
|
||||||
$claimed = @{} # строка → занята областью-диапазоном
|
$claimed = @{} # строка → занята областью-диапазоном
|
||||||
foreach ($a in @($namedAreas | Sort-Object @{ Expression = { $_.BeginRow } }, @{ Expression = { $_.EndRow } })) {
|
foreach ($a in @($namedAreas | Sort-Object @{ Expression = { $_.BeginRow } }, @{ Expression = { $_.EndRow } }, @{ Expression = { $_.Ord } })) {
|
||||||
$fitsBlock = ($a.Type -eq 'Rows' -and $a.BeginRow -ge 0 -and $a.EndRow -ge $a.BeginRow)
|
$fitsBlock = ($a.Type -eq 'Rows' -and $a.BeginRow -ge 0 -and $a.EndRow -ge $a.BeginRow)
|
||||||
if ($fitsBlock) {
|
if ($fitsBlock) {
|
||||||
for ($r = $a.BeginRow; $r -le $a.EndRow; $r++) {
|
for ($r = $a.BeginRow; $r -le $a.EndRow; $r++) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# mxl-decompile v1.16 — Decompile 1C spreadsheet to JSON
|
# mxl-decompile v1.17 — Decompile 1C spreadsheet to JSON
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -308,6 +308,10 @@ def main():
|
|||||||
if val:
|
if val:
|
||||||
fmt["Props"][tag] = val
|
fmt["Props"][tag] = val
|
||||||
continue
|
continue
|
||||||
|
# Вложенный элемент скаляром не является: .text дал бы пустоту, а ps1-порт брал
|
||||||
|
# InnerText и получал склейку поддерева — отсюда расхождение портов.
|
||||||
|
if len(child):
|
||||||
|
continue
|
||||||
val = (child.text or "").strip()
|
val = (child.text or "").strip()
|
||||||
if not val:
|
if not val:
|
||||||
continue
|
continue
|
||||||
@@ -409,6 +413,9 @@ def main():
|
|||||||
return int_of(n) if n is not None else -1
|
return int_of(n) if n is not None else -1
|
||||||
|
|
||||||
named_areas.append({
|
named_areas.append({
|
||||||
|
# Порядок в исходнике — явный ключ сортировки: sorted тут стабилен, а
|
||||||
|
# Sort-Object в ps1 нет, и без него порты расходились.
|
||||||
|
"Ord": len(named_areas),
|
||||||
"Name": text_of(find(ni_node, "d:name")) or "",
|
"Name": text_of(find(ni_node, "d:name")) or "",
|
||||||
"Type": text_of(find(area_node, "d:type")) or "",
|
"Type": text_of(find(area_node, "d:type")) or "",
|
||||||
"BeginRow": coord("beginRow"),
|
"BeginRow": coord("beginRow"),
|
||||||
@@ -535,7 +542,11 @@ def main():
|
|||||||
|
|
||||||
# Свойства формата, которые стилем НЕ являются: ширина принадлежит колонке, высота —
|
# Свойства формата, которые стилем НЕ являются: ширина принадлежит колонке, высота —
|
||||||
# строке, вид заполнения выводится из того, чем задано содержимое ячейки.
|
# строке, вид заполнения выводится из того, чем задано содержимое ячейки.
|
||||||
NON_STYLE_TAGS = ("width", "height", "fillType", "font")
|
# containsValue/valueType/controlType — свойства ЗНАЧЕНИЯ ячейки, а не оформления: стиль
|
||||||
|
# общий на многие ячейки, а они индивидуальны. Компилятор таких ключей не знает, так что
|
||||||
|
# в DSL они были чистым шумом.
|
||||||
|
NON_STYLE_TAGS = ("width", "height", "fillType", "font",
|
||||||
|
"containsValue", "valueType", "controlType")
|
||||||
BORDER_TAGS = ("border", "leftBorder", "topBorder", "rightBorder", "bottomBorder")
|
BORDER_TAGS = ("border", "leftBorder", "topBorder", "rightBorder", "bottomBorder")
|
||||||
|
|
||||||
def line_to_dsl(idx):
|
def line_to_dsl(idx):
|
||||||
@@ -853,7 +864,7 @@ def main():
|
|||||||
block_areas = []
|
block_areas = []
|
||||||
overlay_areas = []
|
overlay_areas = []
|
||||||
claimed = set()
|
claimed = set()
|
||||||
for a in sorted(named_areas, key=lambda x: (x["BeginRow"], x["EndRow"])):
|
for a in sorted(named_areas, key=lambda x: (x["BeginRow"], x["EndRow"], x["Ord"])):
|
||||||
fits = a["Type"] == "Rows" and a["BeginRow"] >= 0 and a["EndRow"] >= a["BeginRow"]
|
fits = a["Type"] == "Rows" and a["BeginRow"] >= 0 and a["EndRow"] >= a["BeginRow"]
|
||||||
if fits:
|
if fits:
|
||||||
for r in range(a["BeginRow"], a["EndRow"] + 1):
|
for r in range(a["BeginRow"], a["EndRow"] + 1):
|
||||||
|
|||||||
Reference in New Issue
Block a user