Repository: fatih/color (language: go)

Issue:
Documentation lies about Println
The documentation for Color.Println says:

> Spaces are always added between operands and a newline is appended.

However, this is not the case. The method uses `c.wrap` around `fmt.Sprint`, which has the following space separation:

> Spaces are added between operands when neither is a string.
[Source](https://pkg.go.dev/fmt#Sprint)

Example problem:

```go
color.New(color.FgCyan).Println("Foo", "Bar")
```

Expected output is "Foo Bar", real output is "FooBar".


Discussion / hints:

Fix patch:
diff --git a/color.go b/color.go
index c423428..81094e8 100644
--- a/color.go
+++ b/color.go
@@ -269,7 +269,7 @@ func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
 // On Windows, users should wrap w with colorable.NewColorable() if w is of
 // type *os.File.
 func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
-	return fmt.Fprintln(w, c.wrap(fmt.Sprint(a...)))
+	return fmt.Fprintln(w, c.wrap(sprintln(a...)))
 }
 
 // Println formats using the default formats for its operands and writes to
@@ -278,7 +278,7 @@ func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
 // encountered. This is the standard fmt.Print() method wrapped with the given
 // color.
 func (c *Color) Println(a ...interface{}) (n int, err error) {
-	return fmt.Fprintln(Output, c.wrap(fmt.Sprint(a...)))
+	return fmt.Fprintln(Output, c.wrap(sprintln(a...)))
 }
 
 // Sprint is just like Print, but returns a string instead of printing it.
@@ -288,7 +288,7 @@ func (c *Color) Sprint(a ...interface{}) string {
 
 // Sprintln is just like Println, but returns a string instead of printing it.
 func (c *Color) Sprintln(a ...interface{}) string {
-	return fmt.Sprintln(c.Sprint(a...))
+	return c.wrap(sprintln(a...)) + "\n"
 }
 
 // Sprintf is just like Printf, but returns a string instead of printing it.
@@ -370,7 +370,7 @@ func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
 // string. Windows users should use this in conjunction with color.Output.
 func (c *Color) SprintlnFunc() func(a ...interface{}) string {
 	return func(a ...interface{}) string {
-		return fmt.Sprintln(c.Sprint(a...))
+		return c.wrap(sprintln(a...)) + "\n"
 	}
 }
 
@@ -648,3 +648,8 @@ func HiCyanString(format string, a ...interface{}) string { return colorString(f
 func HiWhiteString(format string, a ...interface{}) string {
 	return colorString(format, FgHiWhite, a...)
 }
+
+// sprintln is a helper function to format a string with fmt.Sprintln and trim the trailing newline.
+func sprintln(a ...interface{}) string {
+	return strings.TrimSuffix(fmt.Sprintln(a...), "\n")
+}


Test patch:
diff --git a/color_test.go b/color_test.go
index a8cc887..eb76d53 100644
--- a/color_test.go
+++ b/color_test.go
@@ -504,3 +504,34 @@ func TestIssue206_2(t *testing.T) {
 		t.Errorf("Expecting %v, got '%v'\n", expectedResult, result)
 	}
 }
+
+func TestIssue218(t *testing.T) {
+	// Adds a newline to the end of the last string to make sure it isn't trimmed.
+	params := []interface{}{"word1", "word2", "word3", "word4\n"}
+
+	c := New(FgCyan)
+	c.Println(params...)
+
+	var result = c.Sprintln(params...)
+	fmt.Println(params...)
+	fmt.Print(result)
+
+	const expectedResult = "\x1b[36mword1 word2 word3 word4\n\x1b[0m\n"
+
+	if !bytes.Equal([]byte(result), []byte(expectedResult)) {
+		t.Errorf("Sprintln: Expecting %v (%v), got '%v (%v)'\n", expectedResult, []byte(expectedResult), result, []byte(result))
+	}
+
+	fn := c.SprintlnFunc()
+	result = fn(params...)
+	if !bytes.Equal([]byte(result), []byte(expectedResult)) {
+		t.Errorf("SprintlnFunc: Expecting %v (%v), got '%v (%v)'\n", expectedResult, []byte(expectedResult), result, []byte(result))
+	}
+
+	var buf bytes.Buffer
+	c.Fprintln(&buf, params...)
+	result = buf.String()
+	if !bytes.Equal([]byte(result), []byte(expectedResult)) {
+		t.Errorf("Fprintln: Expecting %v (%v), got '%v (%v)'\n", expectedResult, []byte(expectedResult), result, []byte(result))
+	}
+}