Closed davecheney closed 7 years ago
/cc @ChrisHines you might enjoy this challenge. I think we might end up having to create a separate type for each color (there are a limited number in use, so this isn't so gross) so that we can still have something that is a string, but also has a Format method. Something like
type Green string
func (g Green) Format(f fmt.State, c rune) { ... }
I'd love to be able to do this with a function, but I don't know how just yet. Something like
func Green(s string) fmt.Formatter { ... }
Which sounds straight forward, but not in a way that the underlying value in the interface is directly covertable to a string, for example, if Green
is a type, then we can do
greenGreeting := Green("hello") + " world"
But if it's a function that returns a fmt.Formatter
, the above won't work.
type Green string
func (g Green) Format(f fmt.State, c rune) { ... }
greenGreeting := Green("hello") + " world"
fmt.Println(greenGreeting)
What part of the output from the above code should be green? It seems like the intent is that only "hello" would be green, but greenGreeting
has type Green
, so the whole message would get formatted by Green.Format
which would not be able to tell what portion should be green.
Also, the following wouldn't work, and this inconvenience seems to dilute the value of the approach somewhat.
type Green string
type Blue string
func main() {
greeting := Green("hello") + Blue(" world") // invalid operation: Green("hello") + Blue(" world") (mismatched types Green and Blue)
fmt.Printf(greeting)
}
I'm sorry, what I wrote previosly kind of got me high on my own supply. What we have now is something like this (simplified to remove the unimportant parts)
name, value := "Connection", "close"
fmt.Printf("%s: %s\n", Green(name), Cyan(value))
Which is all fine and good, but what is happening is fmt.Printf
is called with something like this
fmt.Printf("%s: %s\n", "\x1b32mConnection\x1b[0m", "\x1b[36mclose\x1b[0m")
Which to my mind has mixed the data and the presentation up. What I'd like to see is something equivalent to
fmt.Printf("%s: %s\n", fmt.Formatter(Green("Connection")), fmt.Formatter(Cyan("close")))
(The fmt.Formatter
conversions are not necessary, just demonstrating that the thing that comes out of Green
/Cyan
should be a fmt.Formatter
)
And if possible the result from Green("Connection")
was still convertible to a string, even if it required an explicit conversion.
var s string = string(Green("hello"))
The goal is to push the decision on which terminal formatting codes to apply to the data until as late as possible, way down in the guts of the fmt.Printf
.
Two questions.
Green("hello")
implemented fmt.Stringer
(in addition to fmt.Formatter
), does that qualify as "convertible to a string"?On Mon, 26 Sep 2016, 00:46 Chris Hines notifications@github.com wrote:
Two questions.
1.
If Green("hello") implemented fmt.Stringer (in addition to fmt.Formatter), does that qualify as "convertible to a string"? 2.
In general, does the solution need to skip color output when writing to a non-tty?
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/davecheney/httpstat/issues/45#issuecomment-249425749, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAcA5v43gj30NVWpXqwDIqdatoNbYnLks5qtokzgaJpZM4KF0ZZ .
Is this along the lines of what you were thinking?
https://play.golang.org/p/REwtOS6cLs
(escape codes work in terminal but not in playground)
I just remembered once seeing a package that tackled this problem by providing a specialized version of the fmt
functions. The package is at https://github.com/nhooyr/color, but the README now says it's deprecated as of last month. I post it here in case it stimulates any new ideas.
I experimented with implementing different coloured strings as types but the result was a net increase in lines and I still had too much generation of a string, adding color tags, passing it around as a string.
I'm unhappy that the presentation (colouring) logic is smeared through the business logic, but I'll suck it up as the alternative is more verbose.
Currently the formatting of coloured output is handled before passing the string to
fmt.Printf
. As an exercise I would like to see if it is possible to make the type of the argument passed tofmt.Printf
implementfmt.Formatter
and handle the colorisation of the output at the point at which it is printed.note to implementors: this has no impact on the performance or memory usage of this program, in fact it'll probably make it infinitesimally slower and larger. I'm mainly interested in exploring this to improve the separation of concerns between the code that prepares the output, and the code that colourises it.