94 lines
2.9 KiB
Go
94 lines
2.9 KiB
Go
package memory
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
fileencoding "reasonix/internal/fileutil/encoding"
|
|
)
|
|
|
|
// quickAddHeading marks the section quick-added notes accumulate under, so
|
|
// repeated "#" additions group together instead of scattering through a
|
|
// hand-written file.
|
|
const quickAddHeading = "## Notes"
|
|
|
|
// AppendDoc appends a one-line note as a bullet under a "## Notes" section in
|
|
// the doc-memory file at path, creating the file (and section) when absent. The
|
|
// note is normalised to a single line so it can't corrupt the section. This is
|
|
// the write side of the "#" quick-add: a plain file edit the user can later
|
|
// reorganise by hand.
|
|
func AppendDoc(path, note string) error {
|
|
note = oneLine(note)
|
|
if note == "" {
|
|
return nil
|
|
}
|
|
if dir := filepath.Dir(path); dir != "" {
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
existing, _ := fileencoding.ReadFileUTF8(path) // missing → new file
|
|
body := string(existing)
|
|
bullet := "- " + note
|
|
|
|
var out string
|
|
switch {
|
|
case strings.TrimSpace(body) == "":
|
|
out = "# Project memory\n\n" + quickAddHeading + "\n\n" + bullet + "\n"
|
|
case strings.Contains(body, quickAddHeading):
|
|
// Insert the bullet at the end of the existing Notes section (before the
|
|
// next heading, or at EOF), keeping additions chronological.
|
|
out = insertUnderHeading(body, quickAddHeading, bullet)
|
|
default:
|
|
out = strings.TrimRight(body, "\n") + "\n\n" + quickAddHeading + "\n\n" + bullet + "\n"
|
|
}
|
|
return os.WriteFile(path, []byte(out), 0o644)
|
|
}
|
|
|
|
// writeDocFile overwrites path with body, creating the parent directory and
|
|
// ensuring a single trailing newline. Used by Set.WriteDoc for the panel's
|
|
// in-place editor (path validation happens in the caller).
|
|
func writeDocFile(path, body string) error {
|
|
if dir := filepath.Dir(path); dir != "" {
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
out := strings.TrimRight(body, "\n") + "\n"
|
|
return os.WriteFile(path, []byte(out), 0o644)
|
|
}
|
|
|
|
// insertUnderHeading appends bullet to the end of the section started by heading
|
|
// — just before the next "## "/"# " heading, or at end of file if none follows.
|
|
func insertUnderHeading(body, heading, bullet string) string {
|
|
lines := strings.Split(body, "\n")
|
|
start := -1
|
|
for i, l := range lines {
|
|
if strings.TrimSpace(l) == heading {
|
|
start = i
|
|
break
|
|
}
|
|
}
|
|
if start < 0 { // shouldn't happen (caller checked Contains), but stay safe
|
|
return strings.TrimRight(body, "\n") + "\n\n" + bullet + "\n"
|
|
}
|
|
end := len(lines)
|
|
for i := start + 1; i < len(lines); i++ {
|
|
if strings.HasPrefix(strings.TrimSpace(lines[i]), "#") {
|
|
end = i
|
|
break
|
|
}
|
|
}
|
|
// Trim trailing blank lines within the section, then place the bullet.
|
|
insert := end
|
|
for insert > start+1 && strings.TrimSpace(lines[insert-1]) == "" {
|
|
insert--
|
|
}
|
|
out := append([]string{}, lines[:insert]...)
|
|
out = append(out, bullet)
|
|
out = append(out, lines[insert:]...)
|
|
return strings.Join(out, "\n")
|
|
}
|