Styled text
The term styled text
refers to text printed out to a terminal (or other ::IO
) with either color or other style (e.g., bold, italic) information. This is done by adding ANSI
escape sequences in a string being printed to the terminal. These escape sequences are not rendered as caracters but add the style information.
Style macros
The easiest way to add style information to a String
in Term
is using the dedicated macros:
using Term
println(@green "this is green")
println(@blue "and this is blue")
print("\n")
println(@bold "this is bold")
println(@underline "and this is underlined")
this is green
and this is blue
this is bold
and this is underlined
To color you can use these macros: @black, @red, @green, @yellow, @blue, @magenta, @cyan, @white, @default
. While for styling you have: @bold @dim @italic @underline
.
Note that the styling macros return a string, so you can combine the resulting strings as you would normally:
println(
@green("This is green") * " and " * @red("this is red")
)
println(
"Make your text $(@underline("stand out"))!"
)
This is green and this is red
Make your text stand out!
With these style macros you can do some simply styling, but it gets clunky when you want to go beyond adding some color. Let's say you want you text to be blue, bold and underlined; do you really need to use three macros?
Of course not, you can use the @style
macro!
mytext = @style "this is my text" blue bold underline
println(mytext)
this is my text
Like the macros you already know, @style
returns a string with the desired style, except that now you can specify multiple styles at once!
Markup text
The basic styling macros are great for handling simple cases where you just want to add a bit of style to a piece of text. More realistically, you might want more control about exactly which parts of your text have what style.
As a way of example, let's say you want every other word of yours string:
str="Every other word has colors!"
to have a color. You could do it with macros:
e = @red "Every"
o = "other"
w = @green "word"
h = "has"
c = @blue "colors!"
print(join((e, o, w, h, c), " "))
Every other word has colors!
but that's awful. String interpolation would also not be of much help here. Instead, it would be great if we could specify styles directly inside a normal string and let Term
figure it out.
Well, that's exactly what we're going to do:
import Term: tprint
tprint(
"[red]Every[/red] other [green]word[/green] has [blue]colors![/blue]"
)
Every other word has colors!
Woah! What just happened!! Two things happened: 1) Term
styling machinery detects strings segments like "[red]Every[/red]"
as meaning that the text between "[...]"
and "[/...]"
should be colored red and 2) tprint
(short for term print) detects this style information and applies it to your text before printing.
Not bad huh? Even better, the style information inside a parentheses can be more than just color:
tprint(
"[bold black underline on_red]So much [gold3 bold]STYLE[/gold3 bold] in this text[/bold black underline on_red]"
)
So much STYLE in this text
that's right, Term.jl
can also color the background of your text (by adding on_C
to your color C
you set it as the background, see colors
page). As you can see you can pass multiple style information tags as space separated words within the "[...]"
. Also, you might have noticed, Term
can also handle nested style tags!
If you just want to use Term.jl
's style functionality, just make sure to read the admonition below. If you're curious about what's happening under the hood, read on below!
The style tags used by Term.jl
have an opening "[style]"
and closing "[/style]"
syntax. The style is applied to everything inbetween. For "[/style]"
to close "[style]"
the text in the parentheses must match exactly (excuding /
), up to the number and position of spaces and the words order. So:
"[red] wohoo [/red]" # works
"[red] wohoo [/red ]" # doesn't
"[bold blue] wohoo [/bold blue]" # works
"[bold blue] wohoo [/blue bold]" # doesn't
Occasionally you can do without the closing tag:
tprint("[red]text")
Term.jl
will add the closing tag to the end of the string for you. Generally though, when multiple styles are applied to the same string, it's better to be explicit in exactly where each style starts and ends.
Under the hood
If you're reading here you're curious about what exactly is happening under the hood. So let's get started. Term.jl
, like rich
in python, defines a simple markup language to specify the style of bits of strings. As we saw, the syntax is very simple with an opening and closing tag specifying the style and marking the start and end of the styled text.
So the first thing that needs to happen is the detection of these markup tags. This is surprisingly hard because there's so many possible combinations. You can have markup tags whose style information varies considerably, you can have nested tags, you can have tags spread across lines and you can have nested tags spread across lines:
tprint(
"""
And [blue] somehow
it [bold red] all [/bold red]
has to [green underline] always
work [/green underline] correctly [/blue]
somehow.
"""
)
And somehow
it all
has to always
work correctly
somehow.
All of this is taken care of by Term.markup.extract_markup
which returns a vector of Term.markup.MarkupTag
objects. Each of these stores the opening and close tags (as SingleTag
objects), the text inbetween them as well as a reference to all MarkupTag
s nested in it.
Normally, you should never use extract_markup
directly. Instead you can let Term.style.apply_style
handle it. When passed a String
, apply_style
will use extract_markup
to extract style information before applying it to the string. This is done one MarkupTag
at the time and recursively (i.e., dealing with nested tags before moving on to the next) until no markup information is found.
After extracting style information, apply_style
replaces the MarkupTag
information with the appropriate ANSI escape codes. This is done by parsing the markup information (the text bewteen [...]
) into a Term.style.MarkupStyle
object which stores the style information. Finally, get_style_codes
get the ANSI codes corresponding to the required style. So in summary:
apply_style("[red]text[/red]")
will return a string with style information
"\e[31mtext\e[39m"
which printed to the console looks like:
text