feat(tui): 清单行表与单行替换(第一轮第五块·核心)

外科式改写的地基(ADR-0013):Get-BakNRetBackupListRow 把清单文本拆成带**行号**的行表,Get-BakNRetBackupListRow 之外的行一字节不动。行表刻意保留注释与空行 —— 只在表里放"真正的条目"会让行号错位,改一行就会连锁改掉别处。

解析器给的 Record.Raw 就是原始行文本,所以"没被编辑的行写回原样"是天然的:注释、对齐、$( ) 表达式、跨行拼接都不会被碰到。这也是它比"解析成对象再序列化"安全的地方。

行号越界**抛错**而不是静默不动:静默不动会制造"保存成功但其实没改",而用户看到的是一份以为改过、其实没改的配置。

判据是 spec 里那条往返断言的文本层形式:每行原样写回后**文本逐字节相同**;改一行时只有那一行不同、注释行数不变、原文本不被就地改动。

验收:断言 13 条;test.ps1 9/9 全绿(5.1 与 7)。
This commit is contained in:
Shuery committed 2026-09-27 16:18:27 +08:00
1 parent 835fb0cf2b
commit 54972ab341
5 files changed
+110

No files matched your search

+41
View File
@@ -1556,6 +1556,47 @@ Test-Case '菜单循环:按数量分档验(0 项 / 1 项 / 3 项),单键
Assert-Equal 'empty' $empty.Reason '原因标为 empty'
}
Test-Case '清单行表与单行替换:只动那一行,注释与其它行逐字节不变' {
$text = @(
'# 注释一',
'Edge',
'',
'+WindowsTerminal :encrypt',
'# 注释二',
'-OldApp :- logs\'
) -join "`n"
# 行表必须包含注释与空行,否则行号会错位(外科式改写按行号动手)
$rows = Get-BakNRetBackupListRow -Text $text
Assert-Equal 6 $rows.Count '总行数'
Assert-Equal 1 $rows[0].Line '首行行号是 1'
Assert-True ($null -eq $rows[0].Record) '注释行不解析'
Assert-True ($null -eq $rows[2].Record) '空行不解析'
Assert-Equal 'Edge' $rows[1].Record.Path '第 2 行是条目'
Assert-Equal 'backup' $rows[3].Record.Direction '第 4 行方向是 backup'
Assert-Equal 'Edge' $rows[1].Raw 'Raw 是原始行文本'
# 往返:每一行原样写回 → 文本必须逐字节相同(这是"外科式"的最强形式)
$same = $text
foreach ($row in $rows) { $same = Set-BakNRetBackupListRow -Text $same -Line $row.Line -NewText $row.Raw }
Assert-Equal $text $same '每行原样写回后文本逐字节相同'
# 改一行:只有那一行不同,注释行数不变,原文本不被就地改动
$edited = Set-BakNRetBackupListRow -Text $text -Line 2 -NewText '-Edge'
$before = @($text -split "`n")
$after = @($edited -split "`n")
Assert-Equal $before.Count $after.Count '行数不变'
Assert-Equal '-Edge' $after[1] '第 2 行改成了 -Edge'
Assert-Equal 2 @(Compare-Object -ReferenceObject $before -DifferenceObject $after).Count '只应当有一行不同'
Assert-Equal 2 @($after | Where-Object { $_.Trim().StartsWith('#') }).Count '注释行数不变'
Assert-Equal 'Edge' $before[1] '原文本没有被就地改动'
# 行号越界必须抛错:静默不动会让"保存成功但其实没改"发生
$threw = $false
try { Set-BakNRetBackupListRow -Text $text -Line 99 -NewText 'x' | Out-Null } catch { $threw = $true }
Assert-True $threw '行号越界必须抛错'
}
# ============================================================================
Remove-Item -LiteralPath $sandbox -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $itemSandbox -Recurse -Force -ErrorAction SilentlyContinue