diff --git a/cmd/generate_changelog/incoming/1830.txt b/cmd/generate_changelog/incoming/1830.txt new file mode 100644 index 00000000..98080fc3 --- /dev/null +++ b/cmd/generate_changelog/incoming/1830.txt @@ -0,0 +1,7 @@ +### PR [#1830](https://github.com/danielmiessler/Fabric/pull/1830) by [ksylvan](https://github.com/ksylvan): Ensure final newline in model generated outputs + +- Feat: ensure newline in `CreateOutputFile` and improve tests +- Add newline to `CreateOutputFile` if missing +- Use `t.Cleanup` for file removal in tests +- Add test for message with trailing newline +- Introduce `printedStream` flag in `Chatter.Send` diff --git a/internal/cli/output.go b/internal/cli/output.go index 117d1fbb..ea64f016 100644 --- a/internal/cli/output.go +++ b/internal/cli/output.go @@ -29,6 +29,9 @@ func CreateOutputFile(message string, fileName string) (err error) { return } defer file.Close() + if !strings.HasSuffix(message, "\n") { + message += "\n" + } if _, err = file.WriteString(message); err != nil { err = fmt.Errorf("%s", fmt.Sprintf(i18n.T("error_writing_to_file"), err)) } else { diff --git a/internal/cli/output_test.go b/internal/cli/output_test.go index 1bfa1e20..f976c34e 100644 --- a/internal/cli/output_test.go +++ b/internal/cli/output_test.go @@ -24,5 +24,34 @@ func TestCreateOutputFile(t *testing.T) { t.Fatalf("CreateOutputFile() error = %v", err) } - defer os.Remove(fileName) + t.Cleanup(func() { os.Remove(fileName) }) + + data, err := os.ReadFile(fileName) + if err != nil { + t.Fatalf("failed to read output file: %v", err) + } + + expected := message + "\n" + if string(data) != expected { + t.Fatalf("expected file contents %q, got %q", expected, data) + } +} + +func TestCreateOutputFileMessageWithTrailingNewline(t *testing.T) { + fileName := "test_output_with_newline.txt" + message := "test message with newline\n" + + if err := CreateOutputFile(message, fileName); err != nil { + t.Fatalf("CreateOutputFile() error = %v", err) + } + t.Cleanup(func() { os.Remove(fileName) }) + + data, err := os.ReadFile(fileName) + if err != nil { + t.Fatalf("failed to read output file: %v", err) + } + + if string(data) != message { + t.Fatalf("expected file contents %q, got %q", message, data) + } } diff --git a/internal/core/chatter.go b/internal/core/chatter.go index a1a60371..9d0d8891 100644 --- a/internal/core/chatter.go +++ b/internal/core/chatter.go @@ -69,6 +69,7 @@ func (o *Chatter) Send(request *domain.ChatRequest, opts *domain.ChatOptions) (s responseChan := make(chan string) errChan := make(chan error, 1) done := make(chan struct{}) + printedStream := false go func() { defer close(done) @@ -81,9 +82,14 @@ func (o *Chatter) Send(request *domain.ChatRequest, opts *domain.ChatOptions) (s message += response if !opts.SuppressThink { fmt.Print(response) + printedStream = true } } + if printedStream && !opts.SuppressThink && !strings.HasSuffix(message, "\n") { + fmt.Println() + } + // Wait for goroutine to finish <-done