hugin/cli/internal/reconcile/block.go
2026-10-27 08:33:21 +01:00

93 lines
2.4 KiB
Go

package reconcile
import (
"strings"
)
// Verwaltete Blöcke in Textdateien wie CLAUDE.md (→ KONZEPT.md 7.2, 11.4):
//
// <!-- hugin:begin — von hugin sync verwaltet, nicht von Hand ändern -->
// @.hugin/rules/pub-hausregeln.md
// <!-- hugin:end -->
func beginMarker(block string) string {
return "<!-- " + block + ":begin — von hugin sync verwaltet, nicht von Hand ändern -->"
}
func endMarker(block string) string { return "<!-- " + block + ":end -->" }
// findBlock liefert die Zeilennummern von Anfangs- und Endmarke, -1 wenn es
// den Block nicht gibt.
func findBlock(lines []string, block string) (begin, end int) {
begin, end = -1, -1
for i, z := range lines {
z = strings.TrimSpace(z)
if begin < 0 && strings.HasPrefix(z, "<!-- "+block+":begin") {
begin = i
} else if begin >= 0 && z == endMarker(block) {
return begin, i
}
}
return -1, -1
}
// readBlock liefert den Inhalt des Blocks mit \n als Zeilenende.
func readBlock(text, block string) (string, bool) {
lines, _ := split(text)
begin, end := findBlock(lines, block)
if begin < 0 {
return "", false
}
var b strings.Builder
for _, z := range lines[begin+1 : end] {
b.WriteString(z + "\n")
}
return b.String(), true
}
// setBlock ersetzt den Block, hängt ihn ans Ende an oder entfernt ihn
// (leerer inhalt). Das Zeilenende der Datei (LF oder CRLF) bleibt erhalten.
func setBlock(text, block, content string) string {
lines, nl := split(text)
begin, end := findBlock(lines, block)
var next []string
if content != "" {
next = append(next, beginMarker(block))
next = append(next, strings.Split(strings.TrimSuffix(content, "\n"), "\n")...)
next = append(next, endMarker(block))
}
switch {
case begin >= 0:
rest := lines[end+1:]
if len(next) == 0 && len(rest) > 0 && strings.TrimSpace(rest[0]) == "" {
rest = rest[1:]
}
lines = append(append(append([]string{}, lines[:begin]...), next...), rest...)
case len(next) > 0:
if len(lines) > 0 && strings.TrimSpace(lines[len(lines)-1]) != "" {
lines = append(lines, "")
}
lines = append(lines, next...)
}
for len(lines) > 0 && strings.TrimSpace(lines[len(lines)-1]) == "" {
lines = lines[:len(lines)-1]
}
if len(lines) == 0 {
return ""
}
return strings.Join(lines, nl) + nl
}
func split(text string) (lines []string, nl string) {
nl = "\n"
if strings.Contains(text, "\r\n") {
nl = "\r\n"
}
text = strings.TrimSuffix(text, nl)
if text == "" {
return nil, nl
}
return strings.Split(text, nl), nl
}