The API for StyledStrings and AnnotatedStrings is considered experimental and is subject to change between Julia versions.
When working with strings, formatting and styling often appear as a secondary concern.
For instance, when printing to a terminal you might want to sprinkleANSI escape sequences in the output, when outputting HTML styling constructs (<span style="...">
, etc.) serve a similar purpose, and so on. It is possible to simply insert the raw styling constructs into the string next to the content itself, but it quickly becomes apparent that this is not well suited for anything but the most basic use cases. Not all terminals support the same ANSI codes, the styling constructs need to be painstakingly removed when calculating the width of already-styled content, and that's before you even get into handling multiple output formats.
Instead of leaving this headache to be widely experienced downstream, it is tackled head-on by the introduction of a special string type (AnnotatedString
). This string type wraps any otherAbstractString
type and allows for formatting information to be applied to regions (e.g. characters 1 through to 7 are bold and red).
Regions of a string are styled by applyingFace
s (think "typeface") to them — a structure that holds styling information. As a convenience, faces in the global faces dictionary (e.g.shadow
) can just be named instead of giving theFace
directly.
Along with these capabilities, we also provide a convenient way for constructingAnnotatedString
s, detailed inStyled String Literals.
julia> using StyledStrings
julia> styled"{yellow:hello} {blue:there}"
"hellothere"
It is sometimes useful to be able to hold metadata relating to regions of a string. AAnnotatedString
wraps another string and allows for regions of it to be annotated with labelled values (:label => value
). All generic string operations are applied to the underlying string. However, when possible, styling information is preserved. This means you can manipulate aAnnotatedString
—taking substrings, padding them, concatenating them with other strings— and the metadata annotations will "come along for the ride".
This string type is fundamental to theStyledStrings stdlib, which uses:face
-labelled annotations to hold styling information.
When concatenating aAnnotatedString
, take care to useannotatedstring
instead ofstring
if you want to keep the string annotations.
julia> str = AnnotatedString("hello there", [(1:5, :word, :greeting), (7:11, :label, 1)])"hello there"julia> length(str)11julia> lpad(str, 14)" hello there"julia> typeof(lpad(str, 7))AnnotatedString{String}julia> str2 = AnnotatedString(" julia", [(2:6, :face, :magenta)])" julia"julia> annotatedstring(str, str2)"hello there julia"julia> str * str2 == annotatedstring(str, str2) # *-concatenation workstrue
The annotations of aAnnotatedString
can be accessed and modified via theannotations
andannotate!
functions.
AnnotatedString
sFace
typeAFace
specifies details of a typeface that text can be set in. It covers a set of basic attributes that generalize well across different formats, namely:
font
height
weight
slant
foreground
background
underline
strikethrough
inverse
inherit
For details on the particular forms these attributes take, see theFace
docstring, but of particular interest isinherit
as it allows you toinherit attributes from otherFace
s.
To make referring to particular styles more convenient, there is a globalDict{Symbol, Face}
that allows forFace
s to be referred to simply by name. Packages can add faces to this dictionary via theaddface!
function, and the loaded faces can be easilycustomized.
Any package registering new faces should ensure that they are prefixed by the package name, i.e. follow the formatmypackage_myface
. This is important for predictability, and to prevent name clashes.
Furthermore, packages should take care to use (and introduce)semantic faces (likecode
) over direct colours and styles (likecyan
). This is helpful in a number of ways, from making the intent in usage more obvious, aiding composability, and making user customisation more intuitive.
There are two set of exemptions to the package-prefix rule:
JuliaSyntaxHighlighting
Basic faces are intended to represent a general idea that is widely applicable.
For setting some text with a certain attribute, we have thebold
,light
,italic
,underline
,strikethrough
, andinverse
faces.
There are also named faces for the 16 terminal colors:black
,red
,green
,yellow
,blue
,magenta
,cyan
,white
,bright_black
/grey
/gray
,bright_red
,bright_green
,bright_blue
,bright_magenta
,bright_cyan
, andbright_white
.
For shadowed text (i.e. dim but there) there is theshadow
face. To indicate a selected region, there is theregion
face. Similarly for emphasis and highlighting theemphasis
andhighlight
faces are defined. There is alsocode
for code-like text.
For visually indicating the severity of messages, theerror
,warning
,success
,info
,note
, andtip
faces are defined.
Faces.toml
)It is good for the name faces in the global face dictionary to be customizable. Theming and aesthetics are nice, and it is important for accessibility reasons too. A TOML file can be parsed into a list ofFace
specifications that are merged with the pre-existing entry in the face dictionary.
AFace
is represented in TOML like so:
[facename]attribute = "value"...[package.facename]attribute = "value"
For example, if theshadow
face is too hard to read it can be made brighter like so:
[shadow]foreground = "white"
On initialization, theconfig/faces.toml
file under the first Julia depot (usually~/.julia
) is loaded.
AnnotatedString
By convention, the:face
attributes of aAnnotatedString
hold information on theFace
s that currently apply. This can be given in multiple forms, as a singleSymbol
naming aFace
s in the global face dictionary, aFace
itself, or a vector of either.
Theshow(::IO, ::MIME"text/plain", ::AnnotatedString)
andshow(::IO, ::MIME"text/html", ::AnnotatedString)
methods both look at the:face
attributes and merge them all together when determining the overall styling.
We can supply:face
attributes to aAnnotatedString
during construction, add them to the properties list afterwards, or use the convenientStyled String literals.
julia> str1 = AnnotatedString("blue text", [(1:9, :face, :blue)])
"blue text"
julia> str2 = styled"{blue:blue text}"
"blue text"
julia> str1 == str2
true
julia> sprint(print, str1, context = :color => true)
"\e[34mblue text\e[39m"
julia> sprint(show, MIME("text/html"), str1, context = :color => true)
"<span style=\"color: #195eb3\">blue text</span>"
To ease construction ofAnnotatedString
s withFace
s applied, thestyled"..."
styled string literal allows for the content and attributes to be easily expressed together via a custom grammar.
Within astyled"..."
literal, curly braces are considered special characters and must be escaped in normal usage (\{
,\}
). This allows them to be used to express annotations with (nestable){annotations...:text}
constructs.
Theannotations...
component is a comma-separated list of three types of annotations.
Face
expressions(key=val,...)
key=value
pairsInterpolation is possible everywhere except for inline face keys.
For more information on the grammar, see the extended help of thestyled"..."
docstring.
As an example, we can demonstrate the list of built-in faces mentioned above like so:
julia> println(styled"The basic font-style attributes are {bold:bold}, {light:light}, {italic:italic},{underline:underline}, and {strikethrough:strikethrough}.In terms of color, we have named faces for the 16 standard terminal colors: {black:■} {red:■} {green:■} {yellow:■} {blue:■} {magenta:■} {cyan:■} {white:■} {bright_black:■} {bright_red:■} {bright_green:■} {bright_yellow:■} {bright_blue:■} {bright_magenta:■} {bright_cyan:■} {bright_white:■}Since {code:bright_black} is effectively grey, we define two aliases for it:{code:grey} and {code:gray} to allow for regional spelling differences.To flip the foreground and background colors of some text, you can use the{code:inverse} face, for example: {magenta:some {inverse:inverse} text}.The intent-based basic faces are {shadow:shadow} (for dim but visible text),{region:region} for selections, {emphasis:emphasis}, and {highlight:highlight}.As above, {code:code} is used for code-like text.Lastly, we have the 'message severity' faces: {error:error}, {warning:warning},{success:success}, {info:info}, {note:note}, and {tip:tip}.Remember that all these faces (and any user or package-defined ones) canarbitrarily nest and overlap, {region,tip:like {bold,italic:so}}.")
The basic font-style attributes arebold,light,italic,underline, andstrikethrough. In terms of color, we have named faces for the 16 standard terminal colors:■■■■■■■■■■■■■■■■ Sincebright_black is effectively grey, we define two aliases for it:grey andgray to allow for regional spelling differences. To flip the foreground and background colors of some text, you can use theinverse face, for example:someinverse text. The intent-based basic faces areshadow (for dim but visible text),region for selections,emphasis, andhighlight. As above,code is used for code-like text. Lastly, we have the 'message severity' faces:error,warning,success,info,note, andtip. Remember that all these faces (and any user or package-defined ones) can arbitrarily nest and overlap,likeso.
StyledStrings.StyledMarkup.@styled_str
—Macro@styled_str -> AnnotatedString
Construct a styled string. Within the string,{<specs>:<content>}
structures apply the formatting to<content>
, according to the list of comma-separated specifications<specs>
. Each spec can either take the form of a face name, an inline face specification, or akey=value
pair. The value must be wrapped by{...}
should it contain any of the characters,=:{}
.
String interpolation with$
functions in the same way as regular strings, except quotes need to be escaped. Faces, keys, and values can also be interpolated with$
.
Example
styled"The {bold:{italic:quick} {(foreground=#cd853f):brown} fox} jumped over the {link={https://en.wikipedia.org/wiki/Laziness}:lazy} dog"
Extended help
This macro can be described by the following EBNF grammar:
styledstring = { styled | interpolated | escaped | plain } ;specialchar = '{' | '}' | '$' | '\"' ;anychar = [\u0-\u1fffff] ;plain = { anychar - specialchar } ;escaped = '\\', specialchar ;interpolated = '$', ? expr ? | '$(', ? expr ?, ')' ;styled = '{', ws, annotations, ':', content, '}' ;content = { interpolated | plain | escaped | styled } ;annotations = annotation | annotations, ws, ',', ws, annotation ;annotation = face | inlineface | keyvalue ;ws = { ' ' | '\t' | '\n' } ; (* whitespace *)face = facename | interpolated ;facename = [A-Za-z0-9_]+ ;inlineface = '(', ws, [ faceprop ], { ws, ',', faceprop }, ws, ')' ;faceprop = [a-z]+, ws, '=', ws, ( [^,)]+ | interpolated) ;keyvalue = key, ws, '=', ws, value ;key = ( [^\0${}=,:], [^\0=,:]* ) | interpolated ;value = simplevalue | curlybraced | interpolated ;curlybraced = '{' { escaped | plain } '}' ;simplevalue = [^${},:], [^,:]* ;
An extra stipulation not encoded in the above grammar is thatplain
should be a valid input tounescape_string
, withspecialchar
kept.
The above grammar forinlineface
is simplified, as the actual implementation is a bit more sophisticated. The full behaviour is given below.
faceprop = ( 'face', ws, '=', ws, ( ? string ? | interpolated ) ) | ( 'height', ws, '=', ws, ( ? number ? | interpolated ) ) | ( 'weight', ws, '=', ws, ( symbol | interpolated ) ) | ( 'slant', ws, '=', ws, ( symbol | interpolated ) ) | ( ( 'foreground' | 'fg' | 'background' | 'bg' ), ws, '=', ws, ( simplecolor | interpolated ) ) | ( 'underline', ws, '=', ws, ( underline | interpolated ) ) | ( 'strikethrough', ws, '=', ws, ( bool | interpolated ) ) | ( 'inverse', ws, '=', ws, ( bool | interpolated ) ) | ( 'inherit', ws, '=', ws, ( inherit | interpolated ) ) ;nothing = 'nothing' ;bool = 'true' | 'false' ;symbol = [^ ,)]+ ;hexcolor = ('#' | '0x'), [0-9a-f]{6} ;simplecolor = hexcolor | symbol | nothing ;underline = nothing | bool | simplecolor | underlinestyled;underlinestyled = '(', ws, ('' | nothing | simplecolor | interpolated), ws, ',', ws, ( symbol | interpolated ), ws ')' ;inherit = ( '[', inheritval, { ',', inheritval }, ']' ) | inheritval;inheritval = ws, ':'?, symbol ;
StyledStrings.StyledMarkup.styled
—Functionstyled(content::AbstractString) -> AnnotatedString
Construct a styled string. Within the string,{<specs>:<content>}
structures apply the formatting to<content>
, according to the list of comma-separated specifications<specs>
. Each spec can either take the form of a face name, an inline face specification, or akey=value
pair. The value must be wrapped by{...}
should it contain any of the characters,=:{}
.
This is a functional equivalent of the@styled_str
macro, just without interpolation capabilities.
StyledStrings.Face
—TypeAFace
is a collection of graphical attributes for displaying text. Faces control how text is displayed in the terminal, and possibly other places too.
Most of the time, aFace
will be stored in the global faces dicts as a unique association with aface name Symbol, and will be most often referred to by this name instead of theFace
object itself.
Attributes
All attributes can be set via the keyword constructor, and default tonothing
.
height
(anInt
orFloat64
): The height in either deci-pt (when anInt
), or as a factor of the base size (when aFloat64
).weight
(aSymbol
): One of the symbols (from faintest to densest):thin
,:extralight
,:light
,:semilight
,:normal
,:medium
,:semibold
,:bold
,:extrabold
, or:black
. In terminals any weight greater than:normal
is displayed as bold, and in terminals that support variable-brightness text, any weight less than:normal
is displayed as faint.slant
(aSymbol
): One of the symbols:italic
,:oblique
, or:normal
.foreground
(aSimpleColor
): The text foreground color.background
(aSimpleColor
): The text background color.underline
, the text underline, which takes one of the following forms:Bool
: Whether the text should be underlined or not.SimpleColor
: The text should be underlined with this color.Tuple{Nothing, Symbol}
: The text should be underlined using the style set by the Symbol, one of:straight
,:double
,:curly
,:dotted
, or:dashed
.Tuple{SimpleColor, Symbol}
: The text should be underlined in the specified SimpleColor, and using the style specified by the Symbol, as before.strikethrough
(aBool
): Whether the text should be struck through.inverse
(aBool
): Whether the foreground and background colors should be inverted.inherit
(aVector{Symbol}
): Names of faces to inherit from, with earlier faces taking priority. All faces inherit from the:default
face.StyledStrings.addface!
—Functionaddface!(name::Symbol => default::Face)
Create a new face by the namename
. So long as no face already exists by this name,default
is added to bothFACES
.default
and (a copy of) toFACES
.current
, with the current value returned.
Should the facename
already exist,nothing
is returned.
Examples
julia> addface!(:mypkg_myface => Face(slant=:italic, underline=true))Face (sample) slant: italic underline: true
StyledStrings.withfaces
—Functionwithfaces(f, kv::Pair...)withfaces(f, kvpair_itr)
Executef
withFACES
.current
temporarily modified by zero or more:name => val
argumentskv
, orkvpair_itr
which produceskv
-form values.
withfaces
is generally used via thewithfaces(kv...) do ... end
syntax. A value ofnothing
can be used to temporarily unset a face (if it has been set). Whenwithfaces
returns, the originalFACES
.current
has been restored.
Examples
julia> withfaces(:yellow => Face(foreground=:red), :green => :blue) do println(styled"{yellow:red} and {green:blue} mixed make {magenta:purple}") endred and blue mixed make purple
StyledStrings.SimpleColor
—Typestruct SimpleColor
A basic representation of a color, intended for string styling purposes. It can either contain a named color (like:red
), or anRGBTuple
which is a NamedTuple specifying anr
,g
,b
color with a bit-depth of 8.
Constructors
SimpleColor(name::Symbol) # e.g. :redSimpleColor(rgb::RGBTuple) # e.g. (r=1, b=2, g=3)SimpleColor(r::Integer, b::Integer, b::Integer)SimpleColor(rgb::UInt32) # e.g. 0x123456
Also seetryparse(SimpleColor, rgb::String)
.
Base.parse
—Methodparse(::Type{SimpleColor}, rgb::String)
An analogue oftryparse(SimpleColor, rgb::String)
(which see), that raises an error instead of returningnothing
.
Base.tryparse
—Methodtryparse(::Type{SimpleColor}, rgb::String)
Attempt to parsergb
as aSimpleColor
. Ifrgb
starts with#
and has a length of 7, it is converted into aRGBTuple
-backedSimpleColor
. Ifrgb
starts witha
-z
,rgb
is interpreted as a color name and converted to aSymbol
-backedSimpleColor
.
Otherwise,nothing
is returned.
Examples
julia> tryparse(SimpleColor, "blue")SimpleColor(blue)julia> tryparse(SimpleColor, "#9558b2")SimpleColor(#9558b2)julia> tryparse(SimpleColor, "#nocolor")
Base.merge
—Methodmerge(initial::Face, others::Face...)
Merge the properties of theinitial
face andothers
, with later faces taking priority.
Settings
This document was generated withDocumenter.jl version 1.8.0 onWednesday 9 July 2025. Using Julia version 1.11.6.