test(check-inline-drift): взять авто-вывод заголовка title_from_name под гвард

Title-FromName/title_from_name продублирован в form-compile и form-decompile: компилятор
пишет заголовок, которого нет в DSL, а декомпилятор на этом же выводе решает, можно ли
заголовок опустить. В комментарии копия заявлена «ТОЧНЫМ зеркалом», но ничем не держалась.
Разъедутся копии — заголовок либо теряется, либо дублируется, и в обоих случаях текст на
форме меняется молча, без ошибки платформы.

Эталон варианта — form-compile: компилятор авторитетен, декомпилятор его зеркалит. Тело
копии приведено к эталону без смены поведения (разбиение строк в ps1; в py — выражение
вместо индексного цикла и `not parts` вместо `len(parts) == 0`; `p.isupper()` и
`p == p.upper()` расходятся только на частях без буквенных символов, а после двух
разделяющих регулярок такая часть недостижима).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSyRNwSiuAAEQCLSCUGEzQ
This commit is contained in:
Nick Shirokov
2026-09-22 20:06:46 +03:00
co-authored by Claude Opus 5
parent cf02c69ff5
commit df47d4d34f
3 changed files with 17 additions and 9 deletions
@@ -413,8 +413,11 @@ function Title-FromName {
[void]$out.Add($parts[0]) [void]$out.Add($parts[0])
for ($i = 1; $i -lt $parts.Count; $i++) { for ($i = 1; $i -lt $parts.Count; $i++) {
$p = $parts[$i] $p = $parts[$i]
if ($p.Length -gt 1 -and $p -ceq $p.ToUpper()) { [void]$out.Add($p) } if ($p.Length -gt 1 -and $p -ceq $p.ToUpper()) {
else { [void]$out.Add($p.ToLower()) } [void]$out.Add($p)
} else {
[void]$out.Add($p.ToLower())
}
} }
return ($out -join ' ') return ($out -join ' ')
} }
@@ -460,15 +460,11 @@ def title_from_name(name):
s = re.sub(r'([А-ЯA-Z])([А-ЯA-Z][а-яa-z])', r'\1 \2', name) s = re.sub(r'([А-ЯA-Z])([А-ЯA-Z][а-яa-z])', r'\1 \2', name)
s = re.sub(r'([а-яa-z0-9])([А-ЯA-Z])', r'\1 \2', s) s = re.sub(r'([а-яa-z0-9])([А-ЯA-Z])', r'\1 \2', s)
parts = s.split(' ') parts = s.split(' ')
if len(parts) == 0: if not parts:
return s return s
out = [parts[0]] out = [parts[0]]
for i in range(1, len(parts)): for p in parts[1:]:
p = parts[i] out.append(p if (len(p) > 1 and p.isupper()) else p.lower())
if len(p) > 1 and p == p.upper():
out.append(p)
else:
out.append(p.lower())
return ' '.join(out) return ' '.join(out)
+9
View File
@@ -525,6 +525,15 @@ const FAMILIES = [
], ],
}, },
// ─── Авто-вывод заголовка из имени элемента формы ────────────────────────
// Компилятор пишет заголовок, которого нет в DSL, декомпилятор на этом же выводе решает, можно
// ли заголовок опустить. Разъехались копии — заголовок либо теряется, либо дублируется, и в
// обоих случаях текст на форме меняется молча, без ошибки платформы (PR #91).
{
name: 'авто-заголовок из имени: title_from_name', py: 'title_from_name', ps1: 'Title-FromName',
variants: [{ id: 'base', authority: 'form-compile', consumers: ['form-decompile'] }],
},
// ─── Компактный JSON декомпиляторов ───────────────────────────────────── // ─── Компактный JSON декомпиляторов ─────────────────────────────────────
// Штатные сериализаторы не годятся: ConvertTo-Json (PS5.1) выравнивает ключи по самому // Штатные сериализаторы не годятся: ConvertTo-Json (PS5.1) выравнивает ключи по самому
// длинному и эскейпит кириллицу в \uXXXX, json.dumps даёт иную раскладку inline/multiline. // длинному и эскейпит кириллицу в \uXXXX, json.dumps даёт иную раскладку inline/multiline.