diff options
Diffstat (limited to 'internal/tmuxedit')
| -rw-r--r-- | internal/tmuxedit/history.go | 30 | ||||
| -rw-r--r-- | internal/tmuxedit/history_test.go | 4 | ||||
| -rw-r--r-- | internal/tmuxedit/run.go | 19 |
3 files changed, 19 insertions, 34 deletions
diff --git a/internal/tmuxedit/history.go b/internal/tmuxedit/history.go index eab4db2..eac3114 100644 --- a/internal/tmuxedit/history.go +++ b/internal/tmuxedit/history.go @@ -9,6 +9,7 @@ import ( "time" "codeberg.org/snonux/hexai/internal/appconfig" + "codeberg.org/snonux/hexai/internal/textutil" ) // HistoryEntry represents a single submission to the AI agent via tmux popup. @@ -52,14 +53,15 @@ func AppendHistory(text, agent, cwd string) error { if err != nil { return fmt.Errorf("cannot open history file: %w", err) } - defer f.Close() + defer func() { _ = f.Close() }() // best-effort on error paths // Write entry if _, err := f.Write(data); err != nil { return fmt.Errorf("cannot write history entry: %w", err) } - return nil + // Check Close error to catch deferred-write failures (e.g. disk full). + return f.Close() } // GetHistory retrieves the most recent history entries (up to limit). @@ -84,7 +86,7 @@ func GetHistory(limit int) ([]HistoryEntry, error) { // Parse JSONL line by line var entries []HistoryEntry - lines := splitLines(data) + lines := textutil.SplitLinesBytes(data) for i, line := range lines { if len(line) == 0 { continue // Skip empty lines @@ -107,25 +109,3 @@ func GetHistory(limit int) ([]HistoryEntry, error) { return entries, nil } - -// splitLines splits data into lines (handles both \n and \r\n) -func splitLines(data []byte) [][]byte { - var lines [][]byte - start := 0 - for i := 0; i < len(data); i++ { - if data[i] == '\n' { - // Include everything before the newline (excluding \r if present) - end := i - if end > start && data[end-1] == '\r' { - end-- - } - lines = append(lines, data[start:end]) - start = i + 1 - } - } - // Handle last line if it doesn't end with newline - if start < len(data) { - lines = append(lines, data[start:]) - } - return lines -} diff --git a/internal/tmuxedit/history_test.go b/internal/tmuxedit/history_test.go index b9d59d3..f6d6d7d 100644 --- a/internal/tmuxedit/history_test.go +++ b/internal/tmuxedit/history_test.go @@ -6,6 +6,8 @@ import ( "path/filepath" "testing" "time" + + "codeberg.org/snonux/hexai/internal/textutil" ) func TestAppendHistory(t *testing.T) { @@ -164,7 +166,7 @@ func TestSplitLines(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := splitLines([]byte(tt.input)) + got := textutil.SplitLinesBytes([]byte(tt.input)) gotStr := make([]string, len(got)) for i, b := range got { gotStr[i] = string(b) diff --git a/internal/tmuxedit/run.go b/internal/tmuxedit/run.go index efa503b..da484df 100644 --- a/internal/tmuxedit/run.go +++ b/internal/tmuxedit/run.go @@ -112,22 +112,23 @@ func loadConfig(configPath string) appconfig.App { var debugLog *log.Logger // initDebugLog creates a debug log file in the state directory -// (~/.local/state/hexai/hexai-tmux-edit.log). Returns an error if the -// state directory cannot be resolved. Silently skips logging if the -// log file itself cannot be opened. -func initDebugLog() error { +// (~/.local/state/hexai/hexai-tmux-edit.log). Returns a closer for the +// log file handle and an error if the state directory cannot be resolved. +// Silently skips logging (returns a no-op closer) if the log file cannot +// be opened. +func initDebugLog() (func(), error) { stateDir, err := appconfig.StateDir() if err != nil { - return fmt.Errorf("cannot create state directory: %w", err) + return nil, fmt.Errorf("cannot create state directory: %w", err) } logPath := filepath.Join(stateDir, "hexai-tmux-edit.log") f, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644) if err != nil { - return nil + return func() {}, nil } debugLog = log.New(f, "", log.LstdFlags|log.Lmicroseconds) - return nil + return func() { _ = f.Close() }, nil } func dbg(format string, args ...any) { @@ -140,9 +141,11 @@ func dbg(format string, args ...any) { // It resolves the agent (by name or auto-detect), extracts the current // prompt, opens the editor popup, then clears and sends the result. func runWithConfig(opts Options, cfg appconfig.App) error { - if err := initDebugLog(); err != nil { + closeLog, err := initDebugLog() + if err != nil { return fmt.Errorf("init debug log: %w", err) } + defer closeLog() dbg("=== hexai-tmux-edit start ===") dbg("opts: pane=%q agent=%q config=%q", opts.Pane, opts.Agent, opts.ConfigPath) |
