Stream: helpdesk (published)

Topic: Critique colorprint idea


view this post on Zulip Andrey Oskin (Feb 23 2022 at 17:36):

Hi!

I want to ask to critique my idea of colorprinting. Maybe it's too complicated, maybe solution already exists in Julia, may be it's ok, but can be done better.

Problem

Sometimes we need colorful output, but current printing facilities like printstyled or Crayons.jl are too low level, verbose and inflexible. By inflexible, I mean that it is very hard to change colorscheme for example, because everything is hard coded. And low level means, that by looking at code it's hard to understand how final output will look like.

Proposal

Make a macro, which accepts following arguments: io, colorscheme and string where string encodes colors inside itself. Example:

@colorprint io colorscheme "{The quick brown fox}:fox jumps over the lazy dog:dog"

Here colorscheme is a Dict of the form Dict(:fox => :brown, :dog => :yellow)

When this macro parse this string, it do one of two things:

  1. If it encounters {...}:<color> group, then color applies to all group
  2. If it encounters word:<color> then color applies only to the word.

So, this string will turn into this:

printstyled(io, "The quick brown fox", color = Dict[:fox])
printstyled(io, " jumps over the lazy ")
printstyled(io, "dog", color = Dict[:dog])

With this approach, we can get

  1. readable printing string (i think that small color tags :fox wouldn't make it less readable)
  2. flexible colorscheme, because it's enough to change one Dict with colors to another to get new output.

Of course, it is more limited then printstyled or Crayons, but if it covers more than 50% usecases I am fine with that.

And of course there is problem of bold etc, but it can be easily solved by changingDict to Dict(:fox => (; color = :brown, bold = true, underline = false)) and splatting in printstyled.

view this post on Zulip Expanding Man (Feb 23 2022 at 17:45):

That sounds pretty good to me. I have two suggestions

If you develop this I'm likely to use it. It never ceases to amaze me how hard it is to print things nicely. My own most recent attempt (which I consider a failure, too hard to use) was ShowCases.jl

view this post on Zulip Expanding Man (Feb 23 2022 at 17:47):

To expand on my comment about @color_str, it makes a better API to have "shallow" macros, though in this case I'm not entirely sure it's possible to do it that way and get performance as good as a macro that converts directly to printstyled calls.

view this post on Zulip Andrey Oskin (Feb 23 2022 at 19:56):

I like the idea of @color_str macro. But since I still want to support colorschemes, resulting interface looks like

print(colorscheme, color"{The quick brown fox}:fox jumps over the lazy dog:dog")

Of course, I can make default dict with relation :black => :black, so one can use default color's naming and get colorized output.

view this post on Zulip Andrey Oskin (Feb 23 2022 at 19:58):

As for the first question, I do not know any standard syntax (that was part of the question, actually). It is possible to use colors in markdown, but it looks long and verbose. I've used similar syntax in few packages already (e.g. https://github.com/JuliaLogging/MiniLoggers.jl), so it looks reasonable enough for me, but I am biased.

view this post on Zulip Expanding Man (Feb 23 2022 at 20:02):

Yeah, it looks reasonable to me to. Maybe there isn't a standard alternative, I don't know

view this post on Zulip Fredrik Ekre (Feb 23 2022 at 20:05):

Personally I think it would be clearer if the color came first.

view this post on Zulip Andrey Oskin (Feb 23 2022 at 20:20):

color"fox:{The quick brown fox} jumps over the lazy dog:dog"?

view this post on Zulip Andrey Oskin (Feb 23 2022 at 20:21):

Ha, dog is a bad example :-)

view this post on Zulip Fredrik Ekre (Feb 23 2022 at 20:21):

Or color"{blue:this text is blue} this is default"

view this post on Zulip Andrey Oskin (Feb 23 2022 at 20:25):

Ah, I see.
Yeah, it's like active voice.
"apply action" to "something", instead of "something" "acted upon"

view this post on Zulip Andrey Oskin (Feb 23 2022 at 20:25):

Yeah, this is good.

view this post on Zulip Andrey Oskin (Feb 23 2022 at 20:26):

I think parsing should be more or less the same.

view this post on Zulip Expanding Man (Feb 23 2022 at 20:31):

I don't really like the color inside of the brackets because blue:this text is blue looks like it only applies to this

view this post on Zulip Expanding Man (Feb 23 2022 at 20:31):

You could maybe go a simplified latex route {\blue this text is blue} this text is default

view this post on Zulip Fredrik Ekre (Feb 23 2022 at 20:32):

I just feel like I have seen the syntax I suggested somewhere.

view this post on Zulip Andrey Oskin (Feb 23 2022 at 20:33):

And something like {\blue\bold this text is blue and bold} this text is default, is it readable?

view this post on Zulip Andrey Oskin (Feb 23 2022 at 20:35):

Hm, and I immediately run into the problem

f(s) = s
macro foo_str(s)
    f(s)
end

x = 1

julia> foo"_$x_"
"_\$x_"

Is it possible to interpolate string, which is used in macro?

view this post on Zulip Andrey Oskin (Feb 23 2022 at 20:35):

Otherwise this idea lose 50% of it's usefulness.

view this post on Zulip Fredrik Ekre (Feb 23 2022 at 20:37):

You have to implement the interpolation yourself.

view this post on Zulip Andrey Oskin (Feb 23 2022 at 20:37):

Any references how to do it? I do not know where to start.

view this post on Zulip Andrey Oskin (Feb 23 2022 at 20:39):

Ah, ok, dump it is.

view this post on Zulip Fredrik Ekre (Feb 23 2022 at 20:40):

I am sure there are better examples, but here is one: search for $ then parse an identifier.

view this post on Zulip Expanding Man (Feb 23 2022 at 20:54):

Andrey Oskin said:

And something like {\blue\bold this text is blue and bold} this text is default, is it readable?

To me yes, but that probably says a lot more about how much of my life I've spent looking at latex than how many people are likely to find it readable

view this post on Zulip Andrey Oskin (Feb 23 2022 at 21:10):

Well, the only problem I see is that backslash interfere with other backslash usage. "\b" is bell as far as I remember, "\n" is caret return and so on. So it's better to use other direction "/blue/bold"

view this post on Zulip Andrey Oskin (Feb 23 2022 at 21:11):

But at this point maybe colon is ok too :)

view this post on Zulip Michael Abbott (Feb 23 2022 at 21:56):

Can the colour dictionary be inserted into io?

One nice thing about colours after the text is that string macros can take a flag at the end, so you could allow a format without the extra brackets:

ioc = IOContext(io, color = Dict(:fox => :brown, :dog => :yellow))
print(ioc, c"this text is blue"blue, " and ", c"{The quick brown fox}:fox jumps over the lazy dog:dog")

Very seldom have I wanted to print colour without interpolating some data or objects, too, so that deserves thought. Should I be able to put :dollar => :red or something in the dictionary? :code => blue for things in backticks?

view this post on Zulip Kwaku Oskin (Mar 13 2022 at 21:55):

Ok, here is some preliminary details. Of course, there are some bugs which should be fixed, but I think that these preliminary details are already rather good.

  1. Colorschemes are working through IOContext, but can be used without it. Depends on how you prefer to work.
  2. Color strings supports nesting. Here is an example

flameshot-2022-03-13T23-54-37.png

flameshot-2022-03-13T23-54-50.png

view this post on Zulip Kwaku Oskin (Mar 13 2022 at 21:57):

  1. It supports interpolation
  2. It supports unicode
using Statistics
v = rand(100);
cs = ColorScheme(:median => :red,  => :yellow)

s = c"""
Median: :median{$(median(v))}
Standard deviation: :δ{$(std(v))}
"""

print(cs, s)

flameshot-2022-03-13T23-56-59.png

view this post on Zulip Kwaku Oskin (Mar 13 2022 at 21:59):

I use : because latex \ interfere with escape symbol. But it can be changed to anything of course.

view this post on Zulip Kwaku Oskin (Mar 13 2022 at 22:01):

Michael Abbott said:

Should I be able to put :dollar => :red or something in the dictionary? :code => blue for things in backticks?

Well, if you want automated resolution of $ or code, then I think it's too complicated. Best way to achieve it is by merging somehow current approach and JuliaParser.jl I think. This is an interesting experiment on it's own, I should give it a thought.

view this post on Zulip Kwaku Oskin (Mar 13 2022 at 22:04):

If anyone is interested, you can find this experimental package in https://github.com/Arkoniak/ColorPrinting.jl

view this post on Zulip Kwaku Oskin (Mar 13 2022 at 22:04):

It is unregistered of course.

view this post on Zulip Filippos Christou (Mar 25 2022 at 16:56):

I didn't go through all the discussion but have you had a look in Term.jl ?

view this post on Zulip Filippos Christou (Mar 25 2022 at 16:57):

It has a very similar syntaxes with the one you suggested. Inspired from the python rich lib.

view this post on Zulip Kwaku Oskin (Mar 29 2022 at 12:16):

Ah, it's nice, but not quite what I want.

  1. Markup tags are rather heavy
  2. There is no colorscheme support (you can do one by providing custom names, but it is always user responsibility)

Thank you for the reference anyway, I can see how this package can be quite popular.


Last updated: Oct 02 2023 at 04:34 UTC