20 days ago
Colored and styled strings for terminals.
Crayons is a package that makes it simple to write strings in different colors and styles to terminals.
It supports the 16 system colors, both the 256 color and 24 bit true color extensions, and the different text styles available to terminals.
The package is designed to perform well, have no dependencies (except
Compat) and load fast (about 10 ms load time after precompilation).
Crayon is created with the keyword only constructor:
Crayon(foreground, background, reset, bold, faint, italics, underline, blink, negative, conceal, strikethrough)
background argument can be of three types:
Symbolrepresenting a color. The available colors are
white. To see the colors in action, try
Crayons.test_system_colors(). These colors are supported by almost all terminals.
Integerbetween 0 and 255. This will use the 256 color ANSI escape codes. To see what number corresponds to what color and if your terminal supports 256 colors, use
Integers, all between 0 and 255. This will be interpreted as a
(r, g, b)24 bit color. To test your terminals support for 24 bit colors, use
Crayons.test_24bit_colors(shownumbers::Bool=false). The support for this is currently quite limited but is being improved in terminals continuously, see here.
The other keyword arguments are all of
Bool type and determine whether the corresponding style should be explicitly enabled or disabled:
reset— reset all styles and colors to default
bold— bold text, also brighten the colors on some terminals
faint— faint text, not widely supported
italics— italic text, not widely supported
underline— underlined text
blink— blinking text
negative— swap the foreground and background
conceal— hides the text, not widely supported
strikethrough— horizontal line through the middle of the text, not widely supported.
To see text with the different styles active, use
By using the symbol
:nothing for any of the keyword arguments, that color or style is inactive and is thus neither actively enable or disabled.
Crayons for the foreground / background version of the 16 system colors as well as the different styles are pre-made and can be found in the
They have the name
<COLOR_NAME>_<BG/FG> for the foreground/background colors and
<STYLE> for the different styles (note the uppercase).
using on the
Crayons.Box module will bring all these into global scope.
The process of printing colored and styled text using Crayons is simple.
By printing a
Crayon to the terminal, the correct code sequences are sent to the terminal such that subsequent printed text takes on the color and style of the printed
For example, try running the code below in the REPL:
print(Crayon(foreground = :red), "In red. ", Crayon(bold = true), "Red and bold") print(Crayon(foreground = 208, background = :red, bold = true), "Orange bold on red") print(Crayon(negative = true, underline = true, bold = true), "Underlined inverse bold") print(Crayon(foreground = (100, 100, 255), background = (255, 255, 0)), "Bluish on yellow") using Crayons.Box print(GREEN_FG, "This is in green") print(BOLD, GREEN_FG, BLUE_BG, "Bold green on blue")
It is also possible to use call overloading on created
Crayon can be called with strings and other
Crayons and the colors and styles will correctly nest.
Correct end sequences will als be printed so the colors and styles are disabled outside the call scope.
This functionality is perhaps more clearly shown with some examples:
using Crayons.Box print(UNDERLINE("This is underlined."), " But this is not") print(RED_FG("Hello ", BLUE_BG("world"), "!!!"), "!!!") print(GREEN_BG("We ", UNDERLINE("are ", MAGENTA_FG("nesting "), "some "), "colors") )
Note: In order for the color sequences to be printed, the Julia REPL needs to have colors activated, either by Julia automatically detecting terminal support or by starting Julia with the
Alternatively, if the
FORCE_COLOR exist, color sequences are printed no matter what.
Two or more
Crayons can be merged resulting in a new
Crayon with all the properties of the merged ones.
This is done with the function
merge(crayons::Crayon...) or by multiplying
Crayons specify the same property then the property of the last
Crayon in the argument list is used:
using Crayons.Box r_fg = Crayon(foreground = :red) g_bg = Crayon(background = :green) merged = merge(r_fg, g_bg) print(merged, "Red foreground on green background!") print(r_fg * g_bg * Crayons.Box.BOLD, "Bold Red foreground on green background!") # Also with call overloading and nesting print(GREEN_FG( "I am a green line ", BOLD * BLUE_FG * UNDERLINE( "with a bold underlined blue substring" ), " that becomes green again!" ))
The Base function
print_with_color is extended so that the first argument can also be a
inv on a
Crayon returns a
Crayon that undos what the
Crayon in the argument to
As an example,
inv(Crayon(bold = true)) returns a
Crayon that disables bold.
If you want to nest colors and styles through function calls there is the
Crayons onto the stack, print text to the stack, and then
Crayons off. The stack will keep track of what
Crayon is currently active.
It is used just like a
stack = CrayonStack() print(stack, "normal text") print(push!(stack, Crayon(foreground = :red)), "in red") print(push!(stack, Crayon(foreground = :blue)), "in blue") print(pop!(stack), "in red again") print(pop!(stack), "normal text")
CrayonStack can also be created in
incremental mode by calling
CrayonStack(incremental = true).
In that case, the
CrayonStack will only print the changes that are needed to go from the previous text state to the new state, which results in less color codes being printed.
However, note that this means that the
CrayonStack need to be printed to the output buffer for all changes that are made to it (i.e. both when
pop! are used).
The example below shows a working example where all the changes to the stack are printed and another example, which gives wrong result, since one change is not printed.
Both the examples below work correctly if
incremental = false.
# Does work io = IOBuffer() stack = CrayonStack(incremental = true) print(io, push!(stack, Crayon(foreground = :red))) print(io, push!(stack, Crayon(foreground = :red))) print(io, stack, "This will be red") print(takebuf_string(io)) # Does not work io = IOBuffer() stack = CrayonStack(incremental = true) push!(stack, Crayon(foreground = :red)) # <- not printing the stack even though we modify it! print(io, push!(stack, Crayon(foreground = :red))) print(io, stack, "This will not be red") print(takebuf_string(io))
The reason why the last example did not work is because the stack notices that there is no change of text state on the second call to
push!, since the foreground was just kept red.
Failing to print the stack after the first
push! meant that the terminal state and the stack state got out of sync.
Kristoffer Carlsson — @KristofferC