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 characters 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 simple 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!
Perhaps you've tried something like tprint("This is {my} text")
and got surprised when the output was "This is text"
. If so, read on. What happened there is that Term.jl
interprets anything with single squared parentheses ({...}
) as style information and removes that from your text output. So in the example it treated {my}
as a markup style tag and removed it from the text, but my
is not a valid style so it was ultimately ignored. If you want to use []
in your text, you simply need to use double brackets: tprint("This is {{my}} text")
will print "This is {my} text"
as expected.
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 in between. For "{/style]"
to close "{style]"
the text in the parentheses must match exactly (excluding /
), 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.
Highlighting
Term provides a highlighting functionality to automatically style text (e.g. coloring numbers, code snippets etc.) The usage is very simple
import Term: load_code_and_highlight, highlight_syntax, highlight, tprint
tprint(highlight("This text has 1 2 3 numbers, a ::Int type and a :symbol"); highlight=false)
This text has 1 2 3 numbers, a ::Int type and a :symbol
pretty easy. Highlighting happens automatically when calling tprint:
tprint("This text has 1 2 3 numbers, a ::Int type and a :symbol")
This text has 1 2 3 numbers, a ::Int type and a :symbol
but you can turn it off with tprint(...; highlight=false)
.
You can also specify how you want some text to be highlighted:
tprint(highlight("This is just some text", :number)) # it will be colored as number!
This is just some text
The colors used for the highlights are specified by the Theme being used as seen a previous section.
You can also highlight syntax from code snippets (only Julia code for now):
tprint(highlight_syntax("""
function foo(x::Int)
x^2
end
"""))
function foo(x::Int)
x^2
end
or just load and highlight a file (showing lines in a specific range):
load_code_and_highlight(".../src/highlight.jl", 125; δ=5) |> tprint
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 are 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.Style.apply_style
which extracts markup style information from your strings and replaces them with the appropriate ANSI escape codes. This is done by parsing the markup information (the text between {...}
) 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