fix(19 навыков): единая финализация XML — стиль исходника и корректный CDATA (#57)

Три остатка волны #57, все в одной точке — блоке записи XML.

1. Порты расходились по EOL. PS гонял документ через XmlWriter с дефолтным
   NewLineHandling.Replace и принудительно переводил весь файл в CRLF, а py
   сохранял стиль исходника: role-compile на LF-ном Configuration.xml давал
   10311 байт против 10062. Ещё в пяти навыках None стоял, но финальной
   нормализации не было, и на LF-исходнике выходил смешанный EOL (meta-edit:
   40 CRLF + 136 одиночных LF) — та же форма, что у исходного дефекта #57.
   Приведено к форме cf-edit.ps1: None + канонизация к LF + целевой перевод
   строки (стиль файла-назначения, для создаваемого файла — канон CRLF).
   Replace не годится как замена: он превращает переводы строк внутри значений
   атрибутов в 
.

2. Гард `if (-notmatch CDATA)` не обрабатывал CDATA, а отказывался от канона
   во всём файле — то есть деградировал до «не сделал ничего». Заменён
   альтернацией: участки CDATA и комментариев возвращаются как есть, замена
   идёт только вне них. На реальных данных поведение не меняется — в корпусе
   из 476 942 XML нет ни одного CDATA и ни одного комментария.

3. py: три реализации одного правила детекта EOL сведены к одной. Мажоритарное
   правило в role-compile/subsystem-compile давало ДРУГОЙ ответ на смешанном
   входе. Дефолты _finalize_xml_bytes для нового файла приведены к канону
   (UTF-8, CRLF, без хвостового перевода); meta-edit срезает хвост в обеих
   ветках, а не только при создании файла.

Попутно, найдено байтовой сверкой портов:
- py вставлял <Role>/<Form>/<Subsystem> с пятью табами вместо трёх —
  подстановка по голому </ChildObjects> удваивала отступ строки; снэпшоты
  этого не видели, так как схлопывают пробелы между тегами;
- py-порты xdto-* писали XML-декларацию одинарными кавычками (так отдаёт
  lxml), платформа и PS пишут двойные.

Регресс: runner 647/647 ps1, 644/647 py (3 skipped), дрейфа снэпшотов нет.
Корпусный раундтрип метаданных: 4897 объектов, match 100%, совпадает с
эталонами захода #57.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-05 21:41:55 +03:00
co-authored by Claude Opus 5
parent 750ba3bb13
commit 3ef0d74158
38 changed files with 366 additions and 234 deletions
+11 -12
View File
@@ -1,4 +1,4 @@
# cf-edit v1.15 — Edit 1C configuration root (Configuration.xml) # cf-edit v1.16 — Edit 1C configuration root (Configuration.xml)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)][Alias('Path')][string]$ConfigPath, [Parameter(Mandatory)][Alias('Path')][string]$ConfigPath,
@@ -163,11 +163,8 @@ function Assert-EditAllowed([string]$targetPath, [string]$require) {
Assert-EditAllowed $resolvedPath 'editable' Assert-EditAllowed $resolvedPath 'editable'
# --- Load XML with PreserveWhitespace --- # --- Load XML with PreserveWhitespace ---
# EOL исходного файла запоминаем ДО разбора: парсер XML по спецификации схлопывает # NB: парсер XML по спецификации схлопывает CRLF в LF, а вставки ниже собираются с
# CRLF в LF, а вставки ниже собираются с явным CRLF — без восстановления в точке # явным CRLF — поэтому EOL приводится к целевому в точке записи (см. финализацию).
# записи LF-файл стал бы смешанным. Канон CRLF относится к файлам, которые мы
# СОЗДАЁМ; правка существующего сохраняет его стиль (#44/#46/#47).
$script:srcEol = if (([System.IO.File]::ReadAllText($resolvedPath)) -match "`r`n") { "`r`n" } else { "`n" }
$script:xmlDoc = New-Object System.Xml.XmlDocument $script:xmlDoc = New-Object System.Xml.XmlDocument
$script:xmlDoc.PreserveWhitespace = $true $script:xmlDoc.PreserveWhitespace = $true
$script:xmlDoc.Load($resolvedPath) $script:xmlDoc.Load($resolvedPath)
@@ -991,12 +988,14 @@ $memStream.Close()
$text = [System.Text.Encoding]::UTF8.GetString($bytes) $text = [System.Text.Encoding]::UTF8.GetString($bytes)
if ($text.Length -gt 0 -and $text[0] -eq [char]0xFEFF) { $text = $text.Substring(1) } if ($text.Length -gt 0 -and $text[0] -eq [char]0xFEFF) { $text = $text.Substring(1) }
$text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"') $text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($text -notmatch '<!\[CDATA\[|<!--') { $text = [regex]::Replace($text, '(?<=\S) />', '/>') } $text = [regex]::Replace($text, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Возвращаем EOL исходного файла: вставки собраны с CRLF, а сам документ мог быть LF. # Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
$text = ($text -replace "`r`n", "`n") -replace "`n", $script:srcEol # новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $resolvedPath) -and ([System.IO.File]::ReadAllText($resolvedPath) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$text = ($text -replace "`r`n", "`n") -replace "`n", $targetEol
$utf8Bom = New-Object System.Text.UTF8Encoding($true) $utf8Bom = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText($resolvedPath, $text, $utf8Bom) [System.IO.File]::WriteAllText($resolvedPath, $text, $utf8Bom)
+8 -7
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# cf-edit v1.15 — Edit 1C configuration root (Configuration.xml) # cf-edit v1.16 — Edit 1C configuration root (Configuration.xml)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
@@ -341,21 +341,22 @@ def _detect_xml_style(path):
def _finalize_xml_bytes(xml_bytes, style): def _finalize_xml_bytes(xml_bytes, style):
"""Привести сериализованные байты к стилю оригинала (или к дефолту, если style is None).""" """Привести байты к стилю оригинала; для НОВОГО файла (style is None) — к канону
enc_decl = style["enc"] if style else "utf-8" выгрузки Конфигуратора: encoding="UTF-8", CRLF в разделителях, без перевода в конце."""
enc_decl = style["enc"] if style else "UTF-8"
xml_bytes = xml_bytes.replace( xml_bytes = xml_bytes.replace(
b"<?xml version='1.0' encoding='UTF-8'?>", b"<?xml version='1.0' encoding='UTF-8'?>",
b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>') b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>')
# Канонизировать переносы к LF (убирает &#13; от \r в tail'ах) # Канонизировать переносы к LF (убирает &#13; от \r в tail'ах)
xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"") xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"")
.replace(b"\r\n", b"\n").replace(b"\r", b"\n")) .replace(b"\r\n", b"\n").replace(b"\r", b"\n"))
# Финальный перенос — как в оригинале (новый файл → есть) # Финальный перенос — как в оригинале (новый файл → нет, канон #57)
want_final_nl = style["final_nl"] if style else True want_final_nl = style["final_nl"] if style else False
xml_bytes = xml_bytes.rstrip(b"\n") xml_bytes = xml_bytes.rstrip(b"\n")
if want_final_nl: if want_final_nl:
xml_bytes += b"\n" xml_bytes += b"\n"
# EOL — как в оригинале (новый файл → LF, текущее поведение) # EOL — как в оригинале (новый файл → CRLF, канон #57)
if style and style["crlf"]: if (style["crlf"] if style else True):
xml_bytes = xml_bytes.replace(b"\n", b"\r\n") xml_bytes = xml_bytes.replace(b"\n", b"\r\n")
return xml_bytes return xml_bytes
@@ -1,4 +1,4 @@
# cfe-borrow v1.14 — Borrow objects from configuration into extension (CFE) # cfe-borrow v1.15 — Borrow objects from configuration into extension (CFE)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)][string]$ExtensionPath, [Parameter(Mandatory)][string]$ExtensionPath,
@@ -917,12 +917,14 @@ function Borrow-Form {
New-Item -ItemType Directory -Path $formXmlDir -Force | Out-Null New-Item -ItemType Directory -Path $formXmlDir -Force | Out-Null
} }
$formXmlFile = Join-Path $formXmlDir "Form.xml" $formXmlFile = Join-Path $formXmlDir "Form.xml"
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
# Здесь источник не XmlWriter, а OuterXml исходного документа — спацовывает так же. # Здесь источник не XmlWriter, а OuterXml исходного документа — спацовывает так же.
$formXmlText = $formXmlSb.ToString() $formXmlText = $formXmlSb.ToString()
if ($formXmlText -notmatch '<!\[CDATA\[|<!--') { $formXmlText = [regex]::Replace($formXmlText, '(?<=\S) />', '/>') } $formXmlText = [regex]::Replace($formXmlText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Файл создаём мы — канон выгрузки: CRLF в разделителях строк.
$formXmlText = ($formXmlText -replace "`r`n", "`n") -replace "`n", "`r`n"
[System.IO.File]::WriteAllText($formXmlFile, $formXmlText, $enc) [System.IO.File]::WriteAllText($formXmlFile, $formXmlText, $enc)
Info " Created: $formXmlFile" Info " Created: $formXmlFile"
@@ -1029,12 +1031,16 @@ function Register-FormInObject {
$text2 = [System.Text.Encoding]::UTF8.GetString($bytes2) $text2 = [System.Text.Encoding]::UTF8.GetString($bytes2)
if ($text2.Length -gt 0 -and $text2[0] -eq [char]0xFEFF) { $text2 = $text2.Substring(1) } if ($text2.Length -gt 0 -and $text2[0] -eq [char]0xFEFF) { $text2 = $text2.Substring(1) }
$text2 = $text2.Replace('encoding="utf-8"', 'encoding="UTF-8"') $text2 = $text2.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($text2 -notmatch '<!\[CDATA\[|<!--') { $text2 = [regex]::Replace($text2, '(?<=\S) />', '/>') } $text2 = [regex]::Replace($text2, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
$utf8Bom2 = New-Object System.Text.UTF8Encoding($true) $utf8Bom2 = New-Object System.Text.UTF8Encoding($true)
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $objFile) -and ([System.IO.File]::ReadAllText($objFile) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$text2 = ($text2 -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($objFile, $text2, $utf8Bom2) [System.IO.File]::WriteAllText($objFile, $text2, $utf8Bom2)
Info " Registered form in: $objFile" Info " Registered form in: $objFile"
} }
@@ -1423,13 +1429,17 @@ function Merge-AttributesIntoObject {
# Insert attributes before </ChildObjects> # Insert attributes before </ChildObjects>
$text3 = $text3 -replace '</ChildObjects>', "${allAttrXml}`r`n`t`t</ChildObjects>" $text3 = $text3 -replace '</ChildObjects>', "${allAttrXml}`r`n`t`t</ChildObjects>"
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
# Стоит ПОСЛЕ вставки реквизитов, чтобы накрыть и их. # Стоит ПОСЛЕ вставки реквизитов, чтобы накрыть и их.
if ($text3 -notmatch '<!\[CDATA\[|<!--') { $text3 = [regex]::Replace($text3, '(?<=\S) />', '/>') } $text3 = [regex]::Replace($text3, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
$utf8Bom3 = New-Object System.Text.UTF8Encoding($true) $utf8Bom3 = New-Object System.Text.UTF8Encoding($true)
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $objFile) -and ([System.IO.File]::ReadAllText($objFile) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$text3 = ($text3 -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($objFile, $text3, $utf8Bom3) [System.IO.File]::WriteAllText($objFile, $text3, $utf8Bom3)
Info " Merged $added attribute(s) into: $objFile" Info " Merged $added attribute(s) into: $objFile"
} }
@@ -1893,12 +1903,16 @@ $memStream.Close()
$text = [System.Text.Encoding]::UTF8.GetString($bytes) $text = [System.Text.Encoding]::UTF8.GetString($bytes)
if ($text.Length -gt 0 -and $text[0] -eq [char]0xFEFF) { $text = $text.Substring(1) } if ($text.Length -gt 0 -and $text[0] -eq [char]0xFEFF) { $text = $text.Substring(1) }
$text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"') $text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($text -notmatch '<!\[CDATA\[|<!--') { $text = [regex]::Replace($text, '(?<=\S) />', '/>') } $text = [regex]::Replace($text, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
$utf8Bom = New-Object System.Text.UTF8Encoding($true) $utf8Bom = New-Object System.Text.UTF8Encoding($true)
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $extResolvedPath) -and ([System.IO.File]::ReadAllText($extResolvedPath) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$text = ($text -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($extResolvedPath, $text, $utf8Bom) [System.IO.File]::WriteAllText($extResolvedPath, $text, $utf8Bom)
Info "Saved: $extResolvedPath" Info "Saved: $extResolvedPath"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# cfe-borrow v1.14 — Borrow objects from configuration into extension (CFE) # cfe-borrow v1.15 — Borrow objects from configuration into extension (CFE)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
@@ -389,21 +389,22 @@ def _detect_xml_style(path):
def _finalize_xml_bytes(xml_bytes, style): def _finalize_xml_bytes(xml_bytes, style):
"""Привести сериализованные байты к стилю оригинала (или к дефолту, если style is None).""" """Привести байты к стилю оригинала; для НОВОГО файла (style is None) — к канону
enc_decl = style["enc"] if style else "utf-8" выгрузки Конфигуратора: encoding="UTF-8", CRLF в разделителях, без перевода в конце."""
enc_decl = style["enc"] if style else "UTF-8"
xml_bytes = xml_bytes.replace( xml_bytes = xml_bytes.replace(
b"<?xml version='1.0' encoding='UTF-8'?>", b"<?xml version='1.0' encoding='UTF-8'?>",
b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>') b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>')
# Канонизировать переносы к LF (убирает &#13; от \r в tail'ах) # Канонизировать переносы к LF (убирает &#13; от \r в tail'ах)
xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"") xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"")
.replace(b"\r\n", b"\n").replace(b"\r", b"\n")) .replace(b"\r\n", b"\n").replace(b"\r", b"\n"))
# Финальный перенос — как в оригинале (новый файл → есть) # Финальный перенос — как в оригинале (новый файл → нет, канон #57)
want_final_nl = style["final_nl"] if style else True want_final_nl = style["final_nl"] if style else False
xml_bytes = xml_bytes.rstrip(b"\n") xml_bytes = xml_bytes.rstrip(b"\n")
if want_final_nl: if want_final_nl:
xml_bytes += b"\n" xml_bytes += b"\n"
# EOL — как в оригинале (новый файл → LF, текущее поведение) # EOL — как в оригинале (новый файл → CRLF, канон #57)
if style and style["crlf"]: if (style["crlf"] if style else True):
xml_bytes = xml_bytes.replace(b"\n", b"\r\n") xml_bytes = xml_bytes.replace(b"\n", b"\r\n")
return xml_bytes return xml_bytes
+10 -5
View File
@@ -1,4 +1,4 @@
# form-add v1.18 — Add managed form to 1C config object # form-add v1.19 — Add managed form to 1C config object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)] [Parameter(Mandatory)]
@@ -601,6 +601,7 @@ if ($SetDefault -or $isFirstFormForPurpose) {
$settings = New-Object System.Xml.XmlWriterSettings $settings = New-Object System.Xml.XmlWriterSettings
$settings.Encoding = $encBom $settings.Encoding = $encBom
$settings.Indent = $false $settings.Indent = $false
$settings.NewLineHandling = [System.Xml.NewLineHandling]::None
# Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки. # Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки.
$memStream = New-Object System.IO.MemoryStream $memStream = New-Object System.IO.MemoryStream
@@ -612,10 +613,14 @@ $xmlText = [System.Text.Encoding]::UTF8.GetString($memStream.ToArray())
$memStream.Close() $memStream.Close()
if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) } if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) }
$xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"') $xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($xmlText -notmatch '<!\[CDATA\[|<!--') { $xmlText = [regex]::Replace($xmlText, '(?<=\S) />', '/>') } $xmlText = [regex]::Replace($xmlText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $objectXmlFull.Path) -and ([System.IO.File]::ReadAllText($objectXmlFull.Path) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$xmlText = ($xmlText -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($objectXmlFull.Path, $xmlText, $encBom) [System.IO.File]::WriteAllText($objectXmlFull.Path, $xmlText, $encBom)
# --- Фаза 5: Вывод --- # --- Фаза 5: Вывод ---
+8 -7
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# form-add v1.18 — Add managed form to 1C config object # form-add v1.19 — Add managed form to 1C config object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
@@ -227,21 +227,22 @@ def _detect_xml_style(path):
def _finalize_xml_bytes(xml_bytes, style): def _finalize_xml_bytes(xml_bytes, style):
"""Привести сериализованные байты к стилю оригинала (или к дефолту, если style is None).""" """Привести байты к стилю оригинала; для НОВОГО файла (style is None) — к канону
enc_decl = style["enc"] if style else "utf-8" выгрузки Конфигуратора: encoding="UTF-8", CRLF в разделителях, без перевода в конце."""
enc_decl = style["enc"] if style else "UTF-8"
xml_bytes = xml_bytes.replace( xml_bytes = xml_bytes.replace(
b"<?xml version='1.0' encoding='UTF-8'?>", b"<?xml version='1.0' encoding='UTF-8'?>",
b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>') b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>')
# Канонизировать переносы к LF (убирает &#13; от \r в tail'ах) # Канонизировать переносы к LF (убирает &#13; от \r в tail'ах)
xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"") xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"")
.replace(b"\r\n", b"\n").replace(b"\r", b"\n")) .replace(b"\r\n", b"\n").replace(b"\r", b"\n"))
# Финальный перенос — как в оригинале (новый файл → есть) # Финальный перенос — как в оригинале (новый файл → нет, канон #57)
want_final_nl = style["final_nl"] if style else True want_final_nl = style["final_nl"] if style else False
xml_bytes = xml_bytes.rstrip(b"\n") xml_bytes = xml_bytes.rstrip(b"\n")
if want_final_nl: if want_final_nl:
xml_bytes += b"\n" xml_bytes += b"\n"
# EOL — как в оригинале (новый файл → LF, текущее поведение) # EOL — как в оригинале (новый файл → CRLF, канон #57)
if style and style["crlf"]: if (style["crlf"] if style else True):
xml_bytes = xml_bytes.replace(b"\n", b"\r\n") xml_bytes = xml_bytes.replace(b"\n", b"\r\n")
return xml_bytes return xml_bytes
@@ -1,4 +1,4 @@
# form-compile v1.181 — Compile 1C managed form from JSON or object metadata # form-compile v1.182 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[string]$JsonPath, [string]$JsonPath,
@@ -6677,6 +6677,7 @@ if ($formsLeaf -eq 'Forms') {
$regSettings = New-Object System.Xml.XmlWriterSettings $regSettings = New-Object System.Xml.XmlWriterSettings
$regSettings.Encoding = $regEnc $regSettings.Encoding = $regEnc
$regSettings.Indent = $false $regSettings.Indent = $false
$regSettings.NewLineHandling = [System.Xml.NewLineHandling]::None
# Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки. # Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки.
$regMem = New-Object System.IO.MemoryStream $regMem = New-Object System.IO.MemoryStream
$regWriter = [System.Xml.XmlWriter]::Create($regMem, $regSettings) $regWriter = [System.Xml.XmlWriter]::Create($regMem, $regSettings)
@@ -6687,10 +6688,14 @@ if ($formsLeaf -eq 'Forms') {
$regMem.Close() $regMem.Close()
if ($regText.Length -gt 0 -and $regText[0] -eq [char]0xFEFF) { $regText = $regText.Substring(1) } if ($regText.Length -gt 0 -and $regText[0] -eq [char]0xFEFF) { $regText = $regText.Substring(1) }
$regText = $regText.Replace('encoding="utf-8"', 'encoding="UTF-8"') $regText = $regText.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($regText -notmatch '<!\[CDATA\[|<!--') { $regText = [regex]::Replace($regText, '(?<=\S) />', '/>') } $regText = [regex]::Replace($regText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $objectXmlPath) -and ([System.IO.File]::ReadAllText($objectXmlPath) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$regText = ($regText -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($objectXmlPath, $regText, $regEnc) [System.IO.File]::WriteAllText($objectXmlPath, $regText, $regEnc)
Write-Host " Registered: <Form>$formName</Form> in $objectName.xml" Write-Host " Registered: <Form>$formName</Form> in $objectName.xml"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# form-compile v1.181 — Compile 1C managed form from JSON or object metadata # form-compile v1.182 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
import copy import copy
@@ -6610,8 +6610,12 @@ def main():
if f'<Form>{form_name}</Form>' not in raw_text: if f'<Form>{form_name}</Form>' not in raw_text:
# Insert before </ChildObjects> # Insert before </ChildObjects>
if '</ChildObjects>' in raw_text: if '</ChildObjects>' in raw_text:
insert_line = f'\t\t\t<Form>{form_name}</Form>' + eol # Отступ вставки берём у закрывающего тега +1 уровень: подстановка
raw_text = raw_text.replace('</ChildObjects>', insert_line + '\t\t</ChildObjects>', 1) # по голому '</ChildObjects>' удваивала бы уже присутствующий отступ
# строки (получалось 5 табов вместо 3 — PS-порт через DOM даёт 3).
raw_text = re.sub(r'([ \t]*)</ChildObjects>',
lambda m: m.group(1) + '\t' + f'<Form>{form_name}</Form>' + eol + m.group(1) + '</ChildObjects>',
raw_text, count=1)
elif '<ChildObjects/>' in raw_text: elif '<ChildObjects/>' in raw_text:
replacement = ('<ChildObjects>' + eol + f'\t\t\t<Form>{form_name}</Form>' + eol + '\t\t</ChildObjects>') replacement = ('<ChildObjects>' + eol + f'\t\t\t<Form>{form_name}</Form>' + eol + '\t\t</ChildObjects>')
raw_text = raw_text.replace('<ChildObjects/>', replacement, 1) raw_text = raw_text.replace('<ChildObjects/>', replacement, 1)
@@ -1,4 +1,4 @@
# form-edit v1.8 — Edit 1C managed form elements # form-edit v1.9 — Edit 1C managed form elements
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)] [Parameter(Mandatory)]
@@ -1387,12 +1387,16 @@ if ($def.elementEvents -and $def.elementEvents.Count -gt 0) {
$content = $xmlDoc.OuterXml $content = $xmlDoc.OuterXml
# Ensure encoding declaration is uppercase UTF-8 # Ensure encoding declaration is uppercase UTF-8
$content = $content -replace '^<\?xml version="1.0" encoding="utf-8"\?>', '<?xml version="1.0" encoding="UTF-8"?>' $content = $content -replace '^<\?xml version="1.0" encoding="utf-8"\?>', '<?xml version="1.0" encoding="UTF-8"?>'
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($content -notmatch '<!\[CDATA\[|<!--') { $content = [regex]::Replace($content, '(?<=\S) />', '/>') } $content = [regex]::Replace($content, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
$enc = New-Object System.Text.UTF8Encoding($true) $enc = New-Object System.Text.UTF8Encoding($true)
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $resolvedFormPath) -and ([System.IO.File]::ReadAllText($resolvedFormPath) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$content = ($content -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($resolvedFormPath, $content, $enc) [System.IO.File]::WriteAllText($resolvedFormPath, $content, $enc)
# === 14. Summary === # === 14. Summary ===
@@ -1,4 +1,4 @@
# form-edit v1.8 — Edit 1C managed form elements (Python port) # form-edit v1.9 — Edit 1C managed form elements (Python port)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
import json import json
@@ -1,4 +1,4 @@
# form-remove v1.6 — Remove form from 1C object # form-remove v1.7 — Remove form from 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)] [Parameter(Mandatory)]
@@ -83,6 +83,7 @@ $encBom = New-Object System.Text.UTF8Encoding($true)
$settings = New-Object System.Xml.XmlWriterSettings $settings = New-Object System.Xml.XmlWriterSettings
$settings.Encoding = $encBom $settings.Encoding = $encBom
$settings.Indent = $false $settings.Indent = $false
$settings.NewLineHandling = [System.Xml.NewLineHandling]::None
# Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки. # Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки.
$memStream = New-Object System.IO.MemoryStream $memStream = New-Object System.IO.MemoryStream
@@ -94,10 +95,14 @@ $xmlText = [System.Text.Encoding]::UTF8.GetString($memStream.ToArray())
$memStream.Close() $memStream.Close()
if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) } if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) }
$xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"') $xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($xmlText -notmatch '<!\[CDATA\[|<!--') { $xmlText = [regex]::Replace($xmlText, '(?<=\S) />', '/>') } $xmlText = [regex]::Replace($xmlText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $rootXmlFull.Path) -and ([System.IO.File]::ReadAllText($rootXmlFull.Path) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$xmlText = ($xmlText -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($rootXmlFull.Path, $xmlText, $encBom) [System.IO.File]::WriteAllText($rootXmlFull.Path, $xmlText, $encBom)
Write-Host "[OK] Форма $FormName удалена из $rootXmlPath" Write-Host "[OK] Форма $FormName удалена из $rootXmlPath"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# form-remove v1.6 — Remove form from 1C object # form-remove v1.7 — Remove form from 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
@@ -30,21 +30,22 @@ def _detect_xml_style(path):
def _finalize_xml_bytes(xml_bytes, style): def _finalize_xml_bytes(xml_bytes, style):
"""Привести сериализованные байты к стилю оригинала (или к дефолту, если style is None).""" """Привести байты к стилю оригинала; для НОВОГО файла (style is None) — к канону
enc_decl = style["enc"] if style else "utf-8" выгрузки Конфигуратора: encoding="UTF-8", CRLF в разделителях, без перевода в конце."""
enc_decl = style["enc"] if style else "UTF-8"
xml_bytes = xml_bytes.replace( xml_bytes = xml_bytes.replace(
b"<?xml version='1.0' encoding='UTF-8'?>", b"<?xml version='1.0' encoding='UTF-8'?>",
b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>') b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>')
# Канонизировать переносы к LF (убирает &#13; от \r в tail'ах) # Канонизировать переносы к LF (убирает &#13; от \r в tail'ах)
xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"") xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"")
.replace(b"\r\n", b"\n").replace(b"\r", b"\n")) .replace(b"\r\n", b"\n").replace(b"\r", b"\n"))
# Финальный перенос — как в оригинале (новый файл → есть) # Финальный перенос — как в оригинале (новый файл → нет, канон #57)
want_final_nl = style["final_nl"] if style else True want_final_nl = style["final_nl"] if style else False
xml_bytes = xml_bytes.rstrip(b"\n") xml_bytes = xml_bytes.rstrip(b"\n")
if want_final_nl: if want_final_nl:
xml_bytes += b"\n" xml_bytes += b"\n"
# EOL — как в оригинале (новый файл → LF, текущее поведение) # EOL — как в оригинале (новый файл → CRLF, канон #57)
if style and style["crlf"]: if (style["crlf"] if style else True):
xml_bytes = xml_bytes.replace(b"\n", b"\r\n") xml_bytes = xml_bytes.replace(b"\n", b"\r\n")
return xml_bytes return xml_bytes
+10 -5
View File
@@ -1,4 +1,4 @@
# help-add v1.14 — Add built-in help to 1C object # help-add v1.15 — Add built-in help to 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)] [Parameter(Mandatory)]
@@ -266,6 +266,7 @@ if (Test-Path $formsDir) {
$settings = New-Object System.Xml.XmlWriterSettings $settings = New-Object System.Xml.XmlWriterSettings
$settings.Encoding = $encBom $settings.Encoding = $encBom
$settings.Indent = $false $settings.Indent = $false
$settings.NewLineHandling = [System.Xml.NewLineHandling]::None
# Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки. # Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки.
$memStream = New-Object System.IO.MemoryStream $memStream = New-Object System.IO.MemoryStream
$writer = [System.Xml.XmlWriter]::Create($memStream, $settings) $writer = [System.Xml.XmlWriter]::Create($memStream, $settings)
@@ -276,10 +277,14 @@ if (Test-Path $formsDir) {
$memStream.Close() $memStream.Close()
if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) } if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) }
$xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"') $xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($xmlText -notmatch '<!\[CDATA\[|<!--') { $xmlText = [regex]::Replace($xmlText, '(?<=\S) />', '/>') } $xmlText = [regex]::Replace($xmlText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $formMeta.FullName) -and ([System.IO.File]::ReadAllText($formMeta.FullName) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$xmlText = ($xmlText -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($formMeta.FullName, $xmlText, $encBom) [System.IO.File]::WriteAllText($formMeta.FullName, $xmlText, $encBom)
Write-Host " IncludeHelpInContents добавлен: $($formMeta.Name)" Write-Host " IncludeHelpInContents добавлен: $($formMeta.Name)"
+8 -7
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# help-add v1.14 — Add built-in help to 1C object # help-add v1.15 — Add built-in help to 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
@@ -222,21 +222,22 @@ def _detect_xml_style(path):
def _finalize_xml_bytes(xml_bytes, style): def _finalize_xml_bytes(xml_bytes, style):
"""Привести сериализованные байты к стилю оригинала (или к дефолту, если style is None).""" """Привести байты к стилю оригинала; для НОВОГО файла (style is None) — к канону
enc_decl = style["enc"] if style else "utf-8" выгрузки Конфигуратора: encoding="UTF-8", CRLF в разделителях, без перевода в конце."""
enc_decl = style["enc"] if style else "UTF-8"
xml_bytes = xml_bytes.replace( xml_bytes = xml_bytes.replace(
b"<?xml version='1.0' encoding='UTF-8'?>", b"<?xml version='1.0' encoding='UTF-8'?>",
b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>') b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>')
# Канонизировать переносы к LF (убирает &#13; от \r в tail'ах) # Канонизировать переносы к LF (убирает &#13; от \r в tail'ах)
xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"") xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"")
.replace(b"\r\n", b"\n").replace(b"\r", b"\n")) .replace(b"\r\n", b"\n").replace(b"\r", b"\n"))
# Финальный перенос — как в оригинале (новый файл → есть) # Финальный перенос — как в оригинале (новый файл → нет, канон #57)
want_final_nl = style["final_nl"] if style else True want_final_nl = style["final_nl"] if style else False
xml_bytes = xml_bytes.rstrip(b"\n") xml_bytes = xml_bytes.rstrip(b"\n")
if want_final_nl: if want_final_nl:
xml_bytes += b"\n" xml_bytes += b"\n"
# EOL — как в оригинале (новый файл → LF, текущее поведение) # EOL — как в оригинале (новый файл → CRLF, канон #57)
if style and style["crlf"]: if (style["crlf"] if style else True):
xml_bytes = xml_bytes.replace(b"\n", b"\r\n") xml_bytes = xml_bytes.replace(b"\n", b"\r\n")
return xml_bytes return xml_bytes
@@ -1,4 +1,4 @@
# interface-edit v1.12 — Edit 1C CommandInterface.xml # interface-edit v1.13 — Edit 1C CommandInterface.xml
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)][Alias('Path')][string]$CIPath, [Parameter(Mandatory)][Alias('Path')][string]$CIPath,
@@ -679,12 +679,16 @@ $memStream.Close()
$text = [System.Text.Encoding]::UTF8.GetString($bytes) $text = [System.Text.Encoding]::UTF8.GetString($bytes)
if ($text.Length -gt 0 -and $text[0] -eq [char]0xFEFF) { $text = $text.Substring(1) } if ($text.Length -gt 0 -and $text[0] -eq [char]0xFEFF) { $text = $text.Substring(1) }
$text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"') $text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($text -notmatch '<!\[CDATA\[|<!--') { $text = [regex]::Replace($text, '(?<=\S) />', '/>') } $text = [regex]::Replace($text, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
$utf8Bom = New-Object System.Text.UTF8Encoding($true) $utf8Bom = New-Object System.Text.UTF8Encoding($true)
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $resolvedPath) -and ([System.IO.File]::ReadAllText($resolvedPath) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$text = ($text -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($resolvedPath, $text, $utf8Bom) [System.IO.File]::WriteAllText($resolvedPath, $text, $utf8Bom)
Info "Saved: $resolvedPath" Info "Saved: $resolvedPath"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# interface-edit v1.12 — Edit 1C CommandInterface.xml # interface-edit v1.13 — Edit 1C CommandInterface.xml
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
@@ -304,21 +304,22 @@ def _detect_xml_style(path):
def _finalize_xml_bytes(xml_bytes, style): def _finalize_xml_bytes(xml_bytes, style):
"""Привести сериализованные байты к стилю оригинала (или к дефолту, если style is None).""" """Привести байты к стилю оригинала; для НОВОГО файла (style is None) — к канону
enc_decl = style["enc"] if style else "utf-8" выгрузки Конфигуратора: encoding="UTF-8", CRLF в разделителях, без перевода в конце."""
enc_decl = style["enc"] if style else "UTF-8"
xml_bytes = xml_bytes.replace( xml_bytes = xml_bytes.replace(
b"<?xml version='1.0' encoding='UTF-8'?>", b"<?xml version='1.0' encoding='UTF-8'?>",
b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>') b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>')
# Канонизировать переносы к LF (убирает &#13; от \r в tail'ах) # Канонизировать переносы к LF (убирает &#13; от \r в tail'ах)
xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"") xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"")
.replace(b"\r\n", b"\n").replace(b"\r", b"\n")) .replace(b"\r\n", b"\n").replace(b"\r", b"\n"))
# Финальный перенос — как в оригинале (новый файл → есть) # Финальный перенос — как в оригинале (новый файл → нет, канон #57)
want_final_nl = style["final_nl"] if style else True want_final_nl = style["final_nl"] if style else False
xml_bytes = xml_bytes.rstrip(b"\n") xml_bytes = xml_bytes.rstrip(b"\n")
if want_final_nl: if want_final_nl:
xml_bytes += b"\n" xml_bytes += b"\n"
# EOL — как в оригинале (новый файл → LF, текущее поведение) # EOL — как в оригинале (новый файл → CRLF, канон #57)
if style and style["crlf"]: if (style["crlf"] if style else True):
xml_bytes = xml_bytes.replace(b"\n", b"\r\n") xml_bytes = xml_bytes.replace(b"\n", b"\r\n")
return xml_bytes return xml_bytes
@@ -1,4 +1,4 @@
# meta-compile v1.82 — Compile 1C metadata object from JSON # meta-compile v1.83 — Compile 1C metadata object from 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)]
@@ -5015,6 +5015,7 @@ if (Test-Path $configXmlPath) {
$cfgSettings = New-Object System.Xml.XmlWriterSettings $cfgSettings = New-Object System.Xml.XmlWriterSettings
$cfgSettings.Encoding = New-Object System.Text.UTF8Encoding($true) $cfgSettings.Encoding = New-Object System.Text.UTF8Encoding($true)
$cfgSettings.Indent = $false $cfgSettings.Indent = $false
$cfgSettings.NewLineHandling = [System.Xml.NewLineHandling]::None
$memStream = New-Object System.IO.MemoryStream $memStream = New-Object System.IO.MemoryStream
$writer = [System.Xml.XmlWriter]::Create($memStream, $cfgSettings) $writer = [System.Xml.XmlWriter]::Create($memStream, $cfgSettings)
$configDoc.Save($writer) $configDoc.Save($writer)
@@ -5024,10 +5025,14 @@ if (Test-Path $configXmlPath) {
$memStream.Close() $memStream.Close()
if ($cfgText.Length -gt 0 -and $cfgText[0] -eq [char]0xFEFF) { $cfgText = $cfgText.Substring(1) } if ($cfgText.Length -gt 0 -and $cfgText[0] -eq [char]0xFEFF) { $cfgText = $cfgText.Substring(1) }
$cfgText = $cfgText.Replace('encoding="utf-8"', 'encoding="UTF-8"') $cfgText = $cfgText.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($cfgText -notmatch '<!\[CDATA\[|<!--') { $cfgText = [regex]::Replace($cfgText, '(?<=\S) />', '/>') } $cfgText = [regex]::Replace($cfgText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $configXmlPath) -and ([System.IO.File]::ReadAllText($configXmlPath) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$cfgText = ($cfgText -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($configXmlPath, $cfgText, (New-Object System.Text.UTF8Encoding($true))) [System.IO.File]::WriteAllText($configXmlPath, $cfgText, (New-Object System.Text.UTF8Encoding($true)))
$regResult = "added" $regResult = "added"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# meta-compile v1.82 — Compile 1C metadata object from JSON # meta-compile v1.83 — Compile 1C metadata object from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
@@ -1,4 +1,4 @@
# meta-edit v1.28 — Edit existing 1C metadata object XML # meta-edit v1.29 — Edit existing 1C metadata object XML
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[string]$DefinitionFile, [string]$DefinitionFile,
@@ -3209,13 +3209,17 @@ if ($text.Length -gt 0 -and $text[0] -eq [char]0xFEFF) {
$text = $text.Substring(1) $text = $text.Substring(1)
} }
$text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"') $text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($text -notmatch '<!\[CDATA\[|<!--') { $text = [regex]::Replace($text, '(?<=\S) />', '/>') } $text = [regex]::Replace($text, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Write with BOM # Write with BOM
$utf8Bom = New-Object System.Text.UTF8Encoding($true) $utf8Bom = New-Object System.Text.UTF8Encoding($true)
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $resolvedPath) -and ([System.IO.File]::ReadAllText($resolvedPath) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$text = ($text -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($resolvedPath, $text, $utf8Bom) [System.IO.File]::WriteAllText($resolvedPath, $text, $utf8Bom)
Info "Saved: $resolvedPath" Info "Saved: $resolvedPath"
+11 -7
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# meta-edit v1.28 — Edit existing 1C metadata object XML # meta-edit v1.29 — Edit existing 1C metadata object XML
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
@@ -2962,21 +2962,22 @@ def _detect_xml_style(path):
def _finalize_xml_bytes(xml_bytes, style): def _finalize_xml_bytes(xml_bytes, style):
"""Привести сериализованные байты к стилю оригинала (или к дефолту, если style is None).""" """Привести байты к стилю оригинала; для НОВОГО файла (style is None) — к канону
enc_decl = style["enc"] if style else "utf-8" выгрузки Конфигуратора: encoding="UTF-8", CRLF в разделителях, без перевода в конце."""
enc_decl = style["enc"] if style else "UTF-8"
xml_bytes = xml_bytes.replace( xml_bytes = xml_bytes.replace(
b"<?xml version='1.0' encoding='UTF-8'?>", b"<?xml version='1.0' encoding='UTF-8'?>",
b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>') b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>')
# Канонизировать переносы к LF (убирает &#13; от \r в tail'ах) # Канонизировать переносы к LF (убирает &#13; от \r в tail'ах)
xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"") xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"")
.replace(b"\r\n", b"\n").replace(b"\r", b"\n")) .replace(b"\r\n", b"\n").replace(b"\r", b"\n"))
# Финальный перенос — как в оригинале (новый файл → есть) # Финальный перенос — как в оригинале (новый файл → нет, канон #57)
want_final_nl = style["final_nl"] if style else True want_final_nl = style["final_nl"] if style else False
xml_bytes = xml_bytes.rstrip(b"\n") xml_bytes = xml_bytes.rstrip(b"\n")
if want_final_nl: if want_final_nl:
xml_bytes += b"\n" xml_bytes += b"\n"
# EOL — как в оригинале (новый файл → LF, текущее поведение) # EOL — как в оригинале (новый файл → CRLF, канон #57)
if style and style["crlf"]: if (style["crlf"] if style else True):
xml_bytes = xml_bytes.replace(b"\n", b"\r\n") xml_bytes = xml_bytes.replace(b"\n", b"\r\n")
return xml_bytes return xml_bytes
@@ -3106,6 +3107,9 @@ def add_predefined_items(items):
'xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' 'xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
f'xsi:type="{xsi_type}" version="{version}">\r\n') f'xsi:type="{xsi_type}" version="{version}">\r\n')
text = hdr + items_xml + '</PredefinedData>' text = hdr + items_xml + '</PredefinedData>'
# Без перевода строки в конце — канон #57. Срезаем в ОБЕИХ ветках: файл, созданный
# прежней версией навыка, мог унести хвост, а PS-порт срезает безусловно.
text = text.rstrip('\r\n')
with open(path, 'wb') as f: with open(path, 'wb') as f:
f.write(b'\xef\xbb\xbf') f.write(b'\xef\xbb\xbf')
f.write(text.encode('utf-8')) f.write(text.encode('utf-8'))
@@ -1,4 +1,4 @@
# meta-remove v1.7 — Remove metadata object from 1C configuration dump # meta-remove v1.8 — Remove metadata object from 1C configuration dump
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)] [Parameter(Mandatory)]
@@ -497,6 +497,7 @@ if (-not $cfgNode) {
$settings = New-Object System.Xml.XmlWriterSettings $settings = New-Object System.Xml.XmlWriterSettings
$settings.Encoding = $enc $settings.Encoding = $enc
$settings.Indent = $false $settings.Indent = $false
$settings.NewLineHandling = [System.Xml.NewLineHandling]::None
$memStream = New-Object System.IO.MemoryStream $memStream = New-Object System.IO.MemoryStream
$writer = [System.Xml.XmlWriter]::Create($memStream, $settings) $writer = [System.Xml.XmlWriter]::Create($memStream, $settings)
$xmlDoc.Save($writer) $xmlDoc.Save($writer)
@@ -506,10 +507,14 @@ if (-not $cfgNode) {
$memStream.Close() $memStream.Close()
if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) } if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) }
$xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"') $xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($xmlText -notmatch '<!\[CDATA\[|<!--') { $xmlText = [regex]::Replace($xmlText, '(?<=\S) />', '/>') } $xmlText = [regex]::Replace($xmlText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $configXml) -and ([System.IO.File]::ReadAllText($configXml) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$xmlText = ($xmlText -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($configXml, $xmlText, $enc) [System.IO.File]::WriteAllText($configXml, $xmlText, $enc)
Write-Host "[OK] Configuration.xml saved" Write-Host "[OK] Configuration.xml saved"
} }
@@ -578,6 +583,7 @@ function Remove-FromSubsystems {
$settings = New-Object System.Xml.XmlWriterSettings $settings = New-Object System.Xml.XmlWriterSettings
$settings.Encoding = $enc $settings.Encoding = $enc
$settings.Indent = $false $settings.Indent = $false
$settings.NewLineHandling = [System.Xml.NewLineHandling]::None
$memStream = New-Object System.IO.MemoryStream $memStream = New-Object System.IO.MemoryStream
$writer = [System.Xml.XmlWriter]::Create($memStream, $settings) $writer = [System.Xml.XmlWriter]::Create($memStream, $settings)
$ssDoc.Save($writer) $ssDoc.Save($writer)
@@ -587,10 +593,14 @@ function Remove-FromSubsystems {
$memStream.Close() $memStream.Close()
if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) } if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) }
$xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"') $xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($xmlText -notmatch '<!\[CDATA\[|<!--') { $xmlText = [regex]::Replace($xmlText, '(?<=\S) />', '/>') } $xmlText = [regex]::Replace($xmlText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $xmlFile.FullName) -and ([System.IO.File]::ReadAllText($xmlFile.FullName) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$xmlText = ($xmlText -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($xmlFile.FullName, $xmlText, $enc) [System.IO.File]::WriteAllText($xmlFile.FullName, $xmlText, $enc)
} }
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# meta-remove v1.7 — Remove metadata object from 1C configuration dump # meta-remove v1.8 — Remove metadata object from 1C configuration dump
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
@@ -292,21 +292,22 @@ def _detect_xml_style(path):
def _finalize_xml_bytes(xml_bytes, style): def _finalize_xml_bytes(xml_bytes, style):
"""Привести сериализованные байты к стилю оригинала (или к дефолту, если style is None).""" """Привести байты к стилю оригинала; для НОВОГО файла (style is None) — к канону
enc_decl = style["enc"] if style else "utf-8" выгрузки Конфигуратора: encoding="UTF-8", CRLF в разделителях, без перевода в конце."""
enc_decl = style["enc"] if style else "UTF-8"
xml_bytes = xml_bytes.replace( xml_bytes = xml_bytes.replace(
b"<?xml version='1.0' encoding='UTF-8'?>", b"<?xml version='1.0' encoding='UTF-8'?>",
b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>') b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>')
# Канонизировать переносы к LF (убирает &#13; от \r в tail'ах) # Канонизировать переносы к LF (убирает &#13; от \r в tail'ах)
xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"") xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"")
.replace(b"\r\n", b"\n").replace(b"\r", b"\n")) .replace(b"\r\n", b"\n").replace(b"\r", b"\n"))
# Финальный перенос — как в оригинале (новый файл → есть) # Финальный перенос — как в оригинале (новый файл → нет, канон #57)
want_final_nl = style["final_nl"] if style else True want_final_nl = style["final_nl"] if style else False
xml_bytes = xml_bytes.rstrip(b"\n") xml_bytes = xml_bytes.rstrip(b"\n")
if want_final_nl: if want_final_nl:
xml_bytes += b"\n" xml_bytes += b"\n"
# EOL — как в оригинале (новый файл → LF, текущее поведение) # EOL — как в оригинале (новый файл → CRLF, канон #57)
if style and style["crlf"]: if (style["crlf"] if style else True):
xml_bytes = xml_bytes.replace(b"\n", b"\r\n") xml_bytes = xml_bytes.replace(b"\n", b"\r\n")
return xml_bytes return xml_bytes
@@ -1,4 +1,4 @@
# role-compile v1.15 — Compile 1C role from JSON # role-compile v1.16 — Compile 1C role from 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)]
@@ -856,6 +856,7 @@ if (Test-Path $configXmlPath) {
$cfgSettings = New-Object System.Xml.XmlWriterSettings $cfgSettings = New-Object System.Xml.XmlWriterSettings
$cfgSettings.Encoding = New-Object System.Text.UTF8Encoding($true) $cfgSettings.Encoding = New-Object System.Text.UTF8Encoding($true)
$cfgSettings.Indent = $false $cfgSettings.Indent = $false
$cfgSettings.NewLineHandling = [System.Xml.NewLineHandling]::None
# Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки. # Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки.
$memStream = New-Object System.IO.MemoryStream $memStream = New-Object System.IO.MemoryStream
$writer = [System.Xml.XmlWriter]::Create($memStream, $cfgSettings) $writer = [System.Xml.XmlWriter]::Create($memStream, $cfgSettings)
@@ -866,10 +867,14 @@ if (Test-Path $configXmlPath) {
$memStream.Close() $memStream.Close()
if ($cfgText.Length -gt 0 -and $cfgText[0] -eq [char]0xFEFF) { $cfgText = $cfgText.Substring(1) } if ($cfgText.Length -gt 0 -and $cfgText[0] -eq [char]0xFEFF) { $cfgText = $cfgText.Substring(1) }
$cfgText = $cfgText.Replace('encoding="utf-8"', 'encoding="UTF-8"') $cfgText = $cfgText.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($cfgText -notmatch '<!\[CDATA\[|<!--') { $cfgText = [regex]::Replace($cfgText, '(?<=\S) />', '/>') } $cfgText = [regex]::Replace($cfgText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $configXmlPath) -and ([System.IO.File]::ReadAllText($configXmlPath) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$cfgText = ($cfgText -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($configXmlPath, $cfgText, (New-Object System.Text.UTF8Encoding($true))) [System.IO.File]::WriteAllText($configXmlPath, $cfgText, (New-Object System.Text.UTF8Encoding($true)))
$regResult = "added" $regResult = "added"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# role-compile v1.15 — Compile 1C role from JSON # role-compile v1.16 — Compile 1C role from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
import json import json
@@ -206,8 +206,10 @@ def detect_format_version(d):
def detect_eol(text): def detect_eol(text):
# Перевод строки ВСТАВКИ берём из самого файла: канон CRLF относится к файлам, # Перевод строки ВСТАВКИ берём из самого файла: канон CRLF относится к файлам,
# которые мы создаём, а правка существующего сохраняет его стиль (#44/#46/#47). # которые мы создаём, а правка существующего сохраняет его стиль (#44/#46/#47).
crlf = text.count('\r\n') # Семантика та же, что у _detect_xml_style в остальных портах: есть CRLF → CRLF.
return '\r\n' if crlf and crlf >= text.count('\n') - crlf else '\n' # Мажоритарное правило здесь было расхождением — на смешанном входе оно давало
# другой ответ, чем канон, при том же назначении.
return '\r\n' if '\r\n' in text else '\n'
def esc_xml(s): def esc_xml(s):
"""Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует """Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует
@@ -820,7 +822,12 @@ def main():
raw_text = raw_text[:insert_pos] + eol + f'\t\t\t{new_role_tag}' + raw_text[insert_pos:] raw_text = raw_text[:insert_pos] + eol + f'\t\t\t{new_role_tag}' + raw_text[insert_pos:]
else: else:
# No existing roles — insert before </ChildObjects> # No existing roles — insert before </ChildObjects>
raw_text = raw_text.replace('</ChildObjects>', f'\t\t\t{new_role_tag}' + eol + '\t\t</ChildObjects>') # Отступ вставки берём у закрывающего тега +1 уровень: подстановка
# по голому '</ChildObjects>' удваивала бы уже присутствующий отступ
# строки (получалось 5 табов вместо 3 — PS-порт через DOM даёт 3).
raw_text = re.sub(r'([ \t]*)</ChildObjects>',
lambda m: m.group(1) + '\t' + new_role_tag + eol + m.group(1) + '</ChildObjects>',
raw_text, count=1)
write_utf8_bom(config_xml_path, raw_text) write_utf8_bom(config_xml_path, raw_text)
reg_result = 'added' reg_result = 'added'
+10 -13
View File
@@ -1,4 +1,4 @@
# skd-edit v1.31 — Atomic 1C DCS editor # skd-edit v1.32 — Atomic 1C DCS editor
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# NB: парный .py собирает выражения автодат вне f-string ради совместимости с python 3.9 (PEP 701). # NB: парный .py собирает выражения автодат вне f-string ради совместимости с python 3.9 (PEP 701).
param( param(
@@ -2192,7 +2192,6 @@ if ($rootOpenMatch.Success) { $script:RawRootOpening = $rootOpenMatch.Value } el
# Detect line ending convention so save can normalize back to whatever the source used. # Detect line ending convention so save can normalize back to whatever the source used.
# 1С Designer writes CRLF on Windows; LF-edited files should stay LF. # 1С Designer writes CRLF on Windows; LF-edited files should stay LF.
$script:LineEnding = if ($script:RawOriginal.Contains("`r`n")) { "`r`n" } else { "`n" }
$xmlDoc = New-Object System.Xml.XmlDocument $xmlDoc = New-Object System.Xml.XmlDocument
$xmlDoc.PreserveWhitespace = $true $xmlDoc.PreserveWhitespace = $true
@@ -4045,18 +4044,16 @@ if ($script:RawRootOpening) {
$content = [regex]::Replace($content, '<DataCompositionSchema\b[^>]*>', { param($m) $script:RawRootOpening }) $content = [regex]::Replace($content, '<DataCompositionSchema\b[^>]*>', { param($m) $script:RawRootOpening })
} }
# (2) Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # (2) Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($content -notmatch '<!\[CDATA\[|<!--') { $content = [regex]::Replace($content, '(?<=\S) />', '/>') } $content = [regex]::Replace($content, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# (3) normalize line endings to match source — operations may mix LF (from new # (3) Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# fragments) with whatever the source used (CRLF on Windows, LF on Linux/git). # новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
if ($script:LineEnding -eq "`r`n") { # Нужно потому, что операции подмешивают LF (новые фрагменты) к стилю источника.
$content = $content -replace '(?<!\r)\n', "`r`n" $targetEol = if ((Test-Path -LiteralPath $resolvedPath) -and ([System.IO.File]::ReadAllText($resolvedPath) -notmatch "`r`n")) { "`n" } else { "`r`n" }
} else { $content = ($content -replace "`r`n", "`n") -replace "`n", $targetEol
$content = $content -replace "`r`n", "`n"
}
$enc = New-Object System.Text.UTF8Encoding($true) $enc = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText($resolvedPath, $content, $enc) [System.IO.File]::WriteAllText($resolvedPath, $content, $enc)
+6 -6
View File
@@ -1,4 +1,4 @@
# skd-edit v1.31 — Atomic 1C DCS editor (Python port) # skd-edit v1.32 — Atomic 1C DCS editor (Python port)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
import json import json
@@ -3441,11 +3441,11 @@ xml_text = xml_bytes.decode("utf-8")
if raw_root_opening: if raw_root_opening:
xml_text = re.sub(r"<DataCompositionSchema\b[^>]*>", lambda m: raw_root_opening, xml_text, count=1, flags=re.DOTALL) xml_text = re.sub(r"<DataCompositionSchema\b[^>]*>", lambda m: raw_root_opening, xml_text, count=1, flags=re.DOTALL)
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>` (lxml и так # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>` (lxml и так
# пишет плотно — правка защитная, чтобы порты оставались байт-эквивалентны). Гард на # пишет плотно — правка защитная, чтобы порты оставались байт-эквивалентны). Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть содержимым, # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if "<![CDATA[" not in xml_text and "<!--" not in xml_text: xml_text = re.sub(r"(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />",
xml_text = re.sub(r"(?<=\S) />", "/>", xml_text) lambda m: "/>" if m.group(0) == " />" else m.group(0), xml_text)
# Канонизировать переносы к LF (убирает возможный &#13;), затем к стилю источника. # Канонизировать переносы к LF (убирает возможный &#13;), затем к стилю источника.
xml_text = xml_text.replace("&#13;\n", "\n").replace("&#13;", "").replace("\r\n", "\n").replace("\r", "\n") xml_text = xml_text.replace("&#13;\n", "\n").replace("&#13;", "").replace("\r\n", "\n").replace("\r", "\n")
@@ -1,4 +1,4 @@
# subsystem-compile v1.16 — Create 1C subsystem from JSON definition # subsystem-compile v1.17 — Create 1C subsystem from JSON definition
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[string]$DefinitionFile, [string]$DefinitionFile,
@@ -657,10 +657,14 @@ if ($parentXmlPath -and (Test-Path $parentXmlPath)) {
$text = [System.Text.Encoding]::UTF8.GetString($bytes) $text = [System.Text.Encoding]::UTF8.GetString($bytes)
if ($text.Length -gt 0 -and $text[0] -eq [char]0xFEFF) { $text = $text.Substring(1) } if ($text.Length -gt 0 -and $text[0] -eq [char]0xFEFF) { $text = $text.Substring(1) }
$text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"') $text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($text -notmatch '<!\[CDATA\[|<!--') { $text = [regex]::Replace($text, '(?<=\S) />', '/>') } $text = [regex]::Replace($text, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $parentXmlPath) -and ([System.IO.File]::ReadAllText($parentXmlPath) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$text = ($text -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($parentXmlPath, $text, $utf8Bom) [System.IO.File]::WriteAllText($parentXmlPath, $text, $utf8Bom)
Write-Host "[OK] Registered in: $parentXmlPath" Write-Host "[OK] Registered in: $parentXmlPath"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# subsystem-compile v1.16 — Create 1C subsystem from JSON definition # subsystem-compile v1.17 — Create 1C subsystem from JSON definition
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
import json import json
@@ -207,8 +207,10 @@ def detect_format_version(d):
def detect_eol(text): def detect_eol(text):
# Перевод строки ВСТАВКИ берём из самого файла: канон CRLF относится к файлам, # Перевод строки ВСТАВКИ берём из самого файла: канон CRLF относится к файлам,
# которые мы создаём, а правка существующего сохраняет его стиль (#44/#46/#47). # которые мы создаём, а правка существующего сохраняет его стиль (#44/#46/#47).
crlf = text.count('\r\n') # Семантика та же, что у _detect_xml_style в остальных портах: есть CRLF → CRLF.
return '\r\n' if crlf and crlf >= text.count('\n') - crlf else '\n' # Мажоритарное правило здесь было расхождением — на смешанном входе оно давало
# другой ответ, чем канон, при том же назначении.
return '\r\n' if '\r\n' in text else '\n'
def esc_xml(s): def esc_xml(s):
"""Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует """Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует
@@ -605,8 +607,12 @@ def main():
replacement = ('<ChildObjects>' + eol + f'\t\t\t<Subsystem>{esc_xml(obj_name)}</Subsystem>' + eol + '\t\t</ChildObjects>') replacement = ('<ChildObjects>' + eol + f'\t\t\t<Subsystem>{esc_xml(obj_name)}</Subsystem>' + eol + '\t\t</ChildObjects>')
raw_text = raw_text.replace('<ChildObjects/>', replacement, 1) raw_text = raw_text.replace('<ChildObjects/>', replacement, 1)
elif '</ChildObjects>' in raw_text: elif '</ChildObjects>' in raw_text:
insert_line = f'\t\t\t<Subsystem>{esc_xml(obj_name)}</Subsystem>' + eol # Отступ вставки берём у закрывающего тега +1 уровень: подстановка
raw_text = raw_text.replace('</ChildObjects>', insert_line + '\t\t</ChildObjects>', 1) # по голому '</ChildObjects>' удваивала бы уже присутствующий отступ
# строки (получалось 5 табов вместо 3 — PS-порт через DOM даёт 3).
raw_text = re.sub(r'([ \t]*)</ChildObjects>',
lambda m: m.group(1) + '\t' + f'<Subsystem>{esc_xml(obj_name)}</Subsystem>' + eol + m.group(1) + '</ChildObjects>',
raw_text, count=1)
write_utf8_bom(parent_xml_path, raw_text) write_utf8_bom(parent_xml_path, raw_text)
print(f"[OK] Registered in: {parent_xml_path}") print(f"[OK] Registered in: {parent_xml_path}")
@@ -1,4 +1,4 @@
# subsystem-edit v1.12 — Edit existing 1C subsystem XML # subsystem-edit v1.13 — Edit existing 1C subsystem XML
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)][Alias('Path')][string]$SubsystemPath, [Parameter(Mandatory)][Alias('Path')][string]$SubsystemPath,
@@ -655,12 +655,16 @@ $memStream.Close()
$text = [System.Text.Encoding]::UTF8.GetString($bytes) $text = [System.Text.Encoding]::UTF8.GetString($bytes)
if ($text.Length -gt 0 -and $text[0] -eq [char]0xFEFF) { $text = $text.Substring(1) } if ($text.Length -gt 0 -and $text[0] -eq [char]0xFEFF) { $text = $text.Substring(1) }
$text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"') $text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($text -notmatch '<!\[CDATA\[|<!--') { $text = [regex]::Replace($text, '(?<=\S) />', '/>') } $text = [regex]::Replace($text, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
$utf8Bom = New-Object System.Text.UTF8Encoding($true) $utf8Bom = New-Object System.Text.UTF8Encoding($true)
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $resolvedPath) -and ([System.IO.File]::ReadAllText($resolvedPath) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$text = ($text -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($resolvedPath, $text, $utf8Bom) [System.IO.File]::WriteAllText($resolvedPath, $text, $utf8Bom)
Info "Saved: $resolvedPath" Info "Saved: $resolvedPath"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# subsystem-edit v1.12 — Edit existing 1C subsystem XML # subsystem-edit v1.13 — Edit existing 1C subsystem XML
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
@@ -452,21 +452,22 @@ def _detect_xml_style(path):
def _finalize_xml_bytes(xml_bytes, style): def _finalize_xml_bytes(xml_bytes, style):
"""Привести сериализованные байты к стилю оригинала (или к дефолту, если style is None).""" """Привести байты к стилю оригинала; для НОВОГО файла (style is None) — к канону
enc_decl = style["enc"] if style else "utf-8" выгрузки Конфигуратора: encoding="UTF-8", CRLF в разделителях, без перевода в конце."""
enc_decl = style["enc"] if style else "UTF-8"
xml_bytes = xml_bytes.replace( xml_bytes = xml_bytes.replace(
b"<?xml version='1.0' encoding='UTF-8'?>", b"<?xml version='1.0' encoding='UTF-8'?>",
b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>') b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>')
# Канонизировать переносы к LF (убирает &#13; от \r в tail'ах) # Канонизировать переносы к LF (убирает &#13; от \r в tail'ах)
xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"") xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"")
.replace(b"\r\n", b"\n").replace(b"\r", b"\n")) .replace(b"\r\n", b"\n").replace(b"\r", b"\n"))
# Финальный перенос — как в оригинале (новый файл → есть) # Финальный перенос — как в оригинале (новый файл → нет, канон #57)
want_final_nl = style["final_nl"] if style else True want_final_nl = style["final_nl"] if style else False
xml_bytes = xml_bytes.rstrip(b"\n") xml_bytes = xml_bytes.rstrip(b"\n")
if want_final_nl: if want_final_nl:
xml_bytes += b"\n" xml_bytes += b"\n"
# EOL — как в оригинале (новый файл → LF, текущее поведение) # EOL — как в оригинале (новый файл → CRLF, канон #57)
if style and style["crlf"]: if (style["crlf"] if style else True):
xml_bytes = xml_bytes.replace(b"\n", b"\r\n") xml_bytes = xml_bytes.replace(b"\n", b"\r\n")
return xml_bytes return xml_bytes
@@ -1,4 +1,4 @@
# template-add v1.16 — Add template to 1C object # template-add v1.17 — Add template to 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)] [Parameter(Mandatory)]
@@ -402,6 +402,7 @@ if ($TemplateType -eq "DataCompositionSchema") {
$settings = New-Object System.Xml.XmlWriterSettings $settings = New-Object System.Xml.XmlWriterSettings
$settings.Encoding = $encBom $settings.Encoding = $encBom
$settings.Indent = $false $settings.Indent = $false
$settings.NewLineHandling = [System.Xml.NewLineHandling]::None
# Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки. # Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки.
$memStream = New-Object System.IO.MemoryStream $memStream = New-Object System.IO.MemoryStream
@@ -413,10 +414,14 @@ $xmlText = [System.Text.Encoding]::UTF8.GetString($memStream.ToArray())
$memStream.Close() $memStream.Close()
if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) } if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) }
$xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"') $xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($xmlText -notmatch '<!\[CDATA\[|<!--') { $xmlText = [regex]::Replace($xmlText, '(?<=\S) />', '/>') } $xmlText = [regex]::Replace($xmlText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $rootXmlFull.Path) -and ([System.IO.File]::ReadAllText($rootXmlFull.Path) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$xmlText = ($xmlText -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($rootXmlFull.Path, $xmlText, $encBom) [System.IO.File]::WriteAllText($rootXmlFull.Path, $xmlText, $encBom)
Write-Host "[OK] Создан макет: $TemplateName ($TemplateType)" Write-Host "[OK] Создан макет: $TemplateName ($TemplateType)"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# template-add v1.16 — Add template to 1C object # template-add v1.17 — Add template to 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
@@ -215,21 +215,22 @@ def _detect_xml_style(path):
def _finalize_xml_bytes(xml_bytes, style): def _finalize_xml_bytes(xml_bytes, style):
"""Привести сериализованные байты к стилю оригинала (или к дефолту, если style is None).""" """Привести байты к стилю оригинала; для НОВОГО файла (style is None) — к канону
enc_decl = style["enc"] if style else "utf-8" выгрузки Конфигуратора: encoding="UTF-8", CRLF в разделителях, без перевода в конце."""
enc_decl = style["enc"] if style else "UTF-8"
xml_bytes = xml_bytes.replace( xml_bytes = xml_bytes.replace(
b"<?xml version='1.0' encoding='UTF-8'?>", b"<?xml version='1.0' encoding='UTF-8'?>",
b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>') b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>')
# Канонизировать переносы к LF (убирает &#13; от \r в tail'ах) # Канонизировать переносы к LF (убирает &#13; от \r в tail'ах)
xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"") xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"")
.replace(b"\r\n", b"\n").replace(b"\r", b"\n")) .replace(b"\r\n", b"\n").replace(b"\r", b"\n"))
# Финальный перенос — как в оригинале (новый файл → есть) # Финальный перенос — как в оригинале (новый файл → нет, канон #57)
want_final_nl = style["final_nl"] if style else True want_final_nl = style["final_nl"] if style else False
xml_bytes = xml_bytes.rstrip(b"\n") xml_bytes = xml_bytes.rstrip(b"\n")
if want_final_nl: if want_final_nl:
xml_bytes += b"\n" xml_bytes += b"\n"
# EOL — как в оригинале (новый файл → LF, текущее поведение) # EOL — как в оригинале (новый файл → CRLF, канон #57)
if style and style["crlf"]: if (style["crlf"] if style else True):
xml_bytes = xml_bytes.replace(b"\n", b"\r\n") xml_bytes = xml_bytes.replace(b"\n", b"\r\n")
return xml_bytes return xml_bytes
@@ -1,4 +1,4 @@
# template-remove v1.5 — Remove template from 1C object # template-remove v1.6 — Remove template from 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)] [Parameter(Mandatory)]
@@ -80,6 +80,7 @@ $encBom = New-Object System.Text.UTF8Encoding($true)
$settings = New-Object System.Xml.XmlWriterSettings $settings = New-Object System.Xml.XmlWriterSettings
$settings.Encoding = $encBom $settings.Encoding = $encBom
$settings.Indent = $false $settings.Indent = $false
$settings.NewLineHandling = [System.Xml.NewLineHandling]::None
# Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки. # Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки.
$memStream = New-Object System.IO.MemoryStream $memStream = New-Object System.IO.MemoryStream
@@ -91,10 +92,14 @@ $xmlText = [System.Text.Encoding]::UTF8.GetString($memStream.ToArray())
$memStream.Close() $memStream.Close()
if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) } if ($xmlText.Length -gt 0 -and $xmlText[0] -eq [char]0xFEFF) { $xmlText = $xmlText.Substring(1) }
$xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"') $xmlText = $xmlText.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($xmlText -notmatch '<!\[CDATA\[|<!--') { $xmlText = [regex]::Replace($xmlText, '(?<=\S) />', '/>') } $xmlText = [regex]::Replace($xmlText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $rootXmlFull.Path) -and ([System.IO.File]::ReadAllText($rootXmlFull.Path) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$xmlText = ($xmlText -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($rootXmlFull.Path, $xmlText, $encBom) [System.IO.File]::WriteAllText($rootXmlFull.Path, $xmlText, $encBom)
Write-Host "[OK] Макет $TemplateName удалён из $rootXmlPath" Write-Host "[OK] Макет $TemplateName удалён из $rootXmlPath"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# template-remove v1.5 — Remove template from 1C object # template-remove v1.6 — Remove template from 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
@@ -30,21 +30,22 @@ def _detect_xml_style(path):
def _finalize_xml_bytes(xml_bytes, style): def _finalize_xml_bytes(xml_bytes, style):
"""Привести сериализованные байты к стилю оригинала (или к дефолту, если style is None).""" """Привести байты к стилю оригинала; для НОВОГО файла (style is None) — к канону
enc_decl = style["enc"] if style else "utf-8" выгрузки Конфигуратора: encoding="UTF-8", CRLF в разделителях, без перевода в конце."""
enc_decl = style["enc"] if style else "UTF-8"
xml_bytes = xml_bytes.replace( xml_bytes = xml_bytes.replace(
b"<?xml version='1.0' encoding='UTF-8'?>", b"<?xml version='1.0' encoding='UTF-8'?>",
b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>') b'<?xml version="1.0" encoding="' + enc_decl.encode("ascii") + b'"?>')
# Канонизировать переносы к LF (убирает &#13; от \r в tail'ах) # Канонизировать переносы к LF (убирает &#13; от \r в tail'ах)
xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"") xml_bytes = (xml_bytes.replace(b"&#13;\n", b"\n").replace(b"&#13;", b"")
.replace(b"\r\n", b"\n").replace(b"\r", b"\n")) .replace(b"\r\n", b"\n").replace(b"\r", b"\n"))
# Финальный перенос — как в оригинале (новый файл → есть) # Финальный перенос — как в оригинале (новый файл → нет, канон #57)
want_final_nl = style["final_nl"] if style else True want_final_nl = style["final_nl"] if style else False
xml_bytes = xml_bytes.rstrip(b"\n") xml_bytes = xml_bytes.rstrip(b"\n")
if want_final_nl: if want_final_nl:
xml_bytes += b"\n" xml_bytes += b"\n"
# EOL — как в оригинале (новый файл → LF, текущее поведение) # EOL — как в оригинале (новый файл → CRLF, канон #57)
if style and style["crlf"]: if (style["crlf"] if style else True):
xml_bytes = xml_bytes.replace(b"\n", b"\r\n") xml_bytes = xml_bytes.replace(b"\n", b"\r\n")
return xml_bytes return xml_bytes
@@ -1,4 +1,4 @@
# xdto-compile v1.5 — Build a 1C XDTO package from an XML Schema (XSD) # xdto-compile v1.6 — Build a 1C XDTO package from an XML Schema (XSD)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory=$true, ParameterSetName='File')] [Parameter(Mandatory=$true, ParameterSetName='File')]
@@ -948,6 +948,7 @@ if (Test-Path $configXmlPath) {
$cfgSettings = New-Object System.Xml.XmlWriterSettings $cfgSettings = New-Object System.Xml.XmlWriterSettings
$cfgSettings.Encoding = New-Object System.Text.UTF8Encoding($true) $cfgSettings.Encoding = New-Object System.Text.UTF8Encoding($true)
$cfgSettings.Indent = $false $cfgSettings.Indent = $false
$cfgSettings.NewLineHandling = [System.Xml.NewLineHandling]::None
# Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки. # Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки.
$memStream = New-Object System.IO.MemoryStream $memStream = New-Object System.IO.MemoryStream
$writer = [System.Xml.XmlWriter]::Create($memStream, $cfgSettings) $writer = [System.Xml.XmlWriter]::Create($memStream, $cfgSettings)
@@ -958,10 +959,14 @@ if (Test-Path $configXmlPath) {
$memStream.Close() $memStream.Close()
if ($cfgText.Length -gt 0 -and $cfgText[0] -eq [char]0xFEFF) { $cfgText = $cfgText.Substring(1) } if ($cfgText.Length -gt 0 -and $cfgText[0] -eq [char]0xFEFF) { $cfgText = $cfgText.Substring(1) }
$cfgText = $cfgText.Replace('encoding="utf-8"', 'encoding="UTF-8"') $cfgText = $cfgText.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($cfgText -notmatch '<!\[CDATA\[|<!--') { $cfgText = [regex]::Replace($cfgText, '(?<=\S) />', '/>') } $cfgText = [regex]::Replace($cfgText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $configXmlPath) -and ([System.IO.File]::ReadAllText($configXmlPath) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$cfgText = ($cfgText -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($configXmlPath, $cfgText, (New-Object System.Text.UTF8Encoding($true))) [System.IO.File]::WriteAllText($configXmlPath, $cfgText, (New-Object System.Text.UTF8Encoding($true)))
$regResult = "added" $regResult = "added"
} }
@@ -1,4 +1,4 @@
# xdto-compile v1.5 — Build a 1C XDTO package from an XML Schema (XSD) (Python port) # xdto-compile v1.6 — Build a 1C XDTO package from an XML Schema (XSD) (Python port)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
import json import json
@@ -960,10 +960,13 @@ if os.path.exists(config_xml):
else: else:
new_elem.tail = child_objects.text new_elem.tail = child_objects.text
data = etree.tostring(cfg_doc, xml_declaration=True, encoding="UTF-8") data = etree.tostring(cfg_doc, xml_declaration=True, encoding="UTF-8")
# lxml пишет декларацию в ОДИНАРНЫХ кавычках, платформа и PS-порт — в двойных.
data = data.replace(b"<?xml version='1.0' encoding='UTF-8'?>",
b'<?xml version="1.0" encoding="UTF-8"?>')
# Парсер XML по спецификации схлопывает CRLF в LF, поэтому tostring отдаёт # Парсер XML по спецификации схлопывает CRLF в LF, поэтому tostring отдаёт
# LF-документ. Возвращаем EOL исходного файла: правка существующего файла # LF-документ. Возвращаем EOL исходного файла: правка существующего файла
# сохраняет его стиль (#44/#46/#47), а .NET-порт делает это через # сохраняет его стиль (#44/#46/#47). Правило то же, что у _detect_xml_style
# NewLineHandling — иначе порты расходятся побайтово. # и у $targetEol в PS-порту: есть CRLF → CRLF.
src_eol = b"\r\n" if b"\r\n" in raw else b"\n" src_eol = b"\r\n" if b"\r\n" in raw else b"\n"
data = data.replace(b"\r\n", b"\n").replace(b"\n", src_eol) data = data.replace(b"\r\n", b"\n").replace(b"\n", src_eol)
if had_bom: if had_bom:
+19 -9
View File
@@ -1,4 +1,4 @@
# xdto-edit v1.3 — Point edits of a 1C XDTO package # xdto-edit v1.4 — Point edits of a 1C XDTO package
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory=$true)] [Parameter(Mandatory=$true)]
@@ -221,6 +221,7 @@ function Edit-Metadata([string]$field, [string]$newValue) {
$settings = New-Object System.Xml.XmlWriterSettings $settings = New-Object System.Xml.XmlWriterSettings
$settings.Encoding = $encBom $settings.Encoding = $encBom
$settings.Indent = $false $settings.Indent = $false
$settings.NewLineHandling = [System.Xml.NewLineHandling]::None
# Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки. # Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки.
$memStream = New-Object System.IO.MemoryStream $memStream = New-Object System.IO.MemoryStream
$writer = [System.Xml.XmlWriter]::Create($memStream, $settings) $writer = [System.Xml.XmlWriter]::Create($memStream, $settings)
@@ -231,10 +232,14 @@ function Edit-Metadata([string]$field, [string]$newValue) {
$memStream.Close() $memStream.Close()
if ($mdText.Length -gt 0 -and $mdText[0] -eq [char]0xFEFF) { $mdText = $mdText.Substring(1) } if ($mdText.Length -gt 0 -and $mdText[0] -eq [char]0xFEFF) { $mdText = $mdText.Substring(1) }
$mdText = $mdText.Replace('encoding="utf-8"', 'encoding="UTF-8"') $mdText = $mdText.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($mdText -notmatch '<!\[CDATA\[|<!--') { $mdText = [regex]::Replace($mdText, '(?<=\S) />', '/>') } $mdText = [regex]::Replace($mdText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $mdFile) -and ([System.IO.File]::ReadAllText($mdFile) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$mdText = ($mdText -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($mdFile, $mdText, $encBom) [System.IO.File]::WriteAllText($mdFile, $mdText, $encBom)
} }
@@ -263,6 +268,7 @@ function Rename-Package([string]$newName) {
if ($found) { if ($found) {
$s = New-Object System.Xml.XmlWriterSettings $s = New-Object System.Xml.XmlWriterSettings
$s.Encoding = $encBom; $s.Indent = $false $s.Encoding = $encBom; $s.Indent = $false
$s.NewLineHandling = [System.Xml.NewLineHandling]::None
# Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки. # Через MemoryStream, а не прямо в файл: нужен шаг пост-обработки строки.
$mem = New-Object System.IO.MemoryStream $mem = New-Object System.IO.MemoryStream
$w = [System.Xml.XmlWriter]::Create($mem, $s) $w = [System.Xml.XmlWriter]::Create($mem, $s)
@@ -271,10 +277,14 @@ function Rename-Package([string]$newName) {
$mem.Close() $mem.Close()
if ($cfgText.Length -gt 0 -and $cfgText[0] -eq [char]0xFEFF) { $cfgText = $cfgText.Substring(1) } if ($cfgText.Length -gt 0 -and $cfgText[0] -eq [char]0xFEFF) { $cfgText = $cfgText.Substring(1) }
$cfgText = $cfgText.Replace('encoding="utf-8"', 'encoding="UTF-8"') $cfgText = $cfgText.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Гард на # Пустой элемент: XmlWriter отдаёт `<a />`, Конфигуратор пишет `<a/>`. Внутри
# CDATA/комментарии: только там `>` не экранируется, и ` />` может быть # CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
# содержимым, а не концом тега. # поэтому они идут первыми ветками альтернации и возвращаются как есть.
if ($cfgText -notmatch '<!\[CDATA\[|<!--') { $cfgText = [regex]::Replace($cfgText, '(?<=\S) />', '/>') } $cfgText = [regex]::Replace($cfgText, '(?s)<!\[CDATA\[.*?\]\]>|<!--.*?-->|(?<=\S) />', { param($m) if ($m.Value -eq ' />') { '/>' } else { $m.Value } })
# Целевой перевод строки: стиль файла-назначения — правка наследует его (#44/#46/#47),
# новый файл получает канон выгрузки CRLF. Зеркало _detect_xml_style в py-порту.
$targetEol = if ((Test-Path -LiteralPath $configXml) -and ([System.IO.File]::ReadAllText($configXml) -notmatch "`r`n")) { "`n" } else { "`r`n" }
$cfgText = ($cfgText -replace "`r`n", "`n") -replace "`n", $targetEol
[System.IO.File]::WriteAllText($configXml, $cfgText, $encBom) [System.IO.File]::WriteAllText($configXml, $cfgText, $encBom)
Write-Host " Configuration.xml: <XDTOPackage> переименован в $newName" Write-Host " Configuration.xml: <XDTOPackage> переименован в $newName"
} else { } else {
@@ -1,4 +1,4 @@
# xdto-edit v1.3 — Point edits of a 1C XDTO package (Python port) # xdto-edit v1.4 — Point edits of a 1C XDTO package (Python port)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
import json import json
@@ -194,10 +194,13 @@ def invoke_sibling(script, argv, what):
def save_xml(doc, path): def save_xml(doc, path):
raw = etree.tostring(doc, xml_declaration=True, encoding="UTF-8") raw = etree.tostring(doc, xml_declaration=True, encoding="UTF-8")
# lxml пишет декларацию в ОДИНАРНЫХ кавычках, платформа и PS-порт — в двойных.
raw = raw.replace(b"<?xml version='1.0' encoding='UTF-8'?>",
b'<?xml version="1.0" encoding="UTF-8"?>')
# Парсер XML по спецификации схлопывает CRLF в LF, поэтому tostring отдаёт # Парсер XML по спецификации схлопывает CRLF в LF, поэтому tostring отдаёт
# LF-документ. Возвращаем EOL исходного файла: правка существующего файла # LF-документ. Возвращаем EOL файла-назначения: правка существующего файла
# сохраняет его стиль (#44/#46/#47), а .NET-порт делает это через # сохраняет его стиль (#44/#46/#47), новый получает канон CRLF. Правило то же,
# NewLineHandling — иначе порты расходятся побайтово. # что у _detect_xml_style и у $targetEol в PS-порту: есть CRLF → CRLF.
src_eol = b"\r\n" src_eol = b"\r\n"
try: try:
with open(path, "rb") as f: with open(path, "rb") as f: