StyledStrings
Applying Style
When working with strings, formatting and applying style often take a back seat.
For example, when outputting to a terminal, you may need to add https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_ (Select_Graphic_Rendition)_parameters[ANSI escape sequences] in the output when the output constructs are in HTML style (<span style="...">
, etc.) serve a similar purpose, and so on. You can simply paste the raw style constructs into the row next to the content itself, but it quickly becomes clear that this option is suitable only for the simplest use cases. Not all terminals support the same ANSI codes, and style constructs need to be painstakingly removed when calculating the width of already stylized content, not to mention working with multiple output formats.
This task can be solved by introducing a special string type (AnnotatedString
). This string type encloses any other type. AbstractString
and allows formatting information to be applied to areas (for example, characters 1 through 7 are highlighted in bold and red).
Areas of the string are styled by applying fonts to them (Face
) — a structure that contains information about the style. For convenience, fonts in the global font dictionary (for example, shadow
) can simply be named instead of passing the font (Face
) directly.
Along with these features, we also provide a convenient way to build AnnotatedString
, described in detail in the section Literals of stylized strings.
julia> using StyledStrings
julia> styled"{yellow:hello} {blue:there}"
"hello there"
Annotated lines
Sometimes it is useful to be able to store metadata related to areas of a string. AnnotatedString'
wraps another string and allows you to annotate its areas with labeled values (+:label ⇒ value+). All universal string operations are applied to the base string. However, when possible, the style information is saved. This means that you can work with `AnnotatedString
— take substrings, complement them, combine them with other strings — and metadata annotations will be attached.
This string type is the main one for [StyledStrings stdlib] (StyledStrings.md#stdlib-styledstrings), which uses annotations marked :face
, for storing information about the style.
When concatenating AnnotatedString
try to use annotatedstring
instead of string
, if you want to save string annotations.
julia> str = AnnotatedString("hello there", [(1:5, :word, :greeting), (7:11, :label, 1)])
"hello there"
julia> length(str)
11
julia> 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) # *-конкатенация работает
true
Access to annotations AnnotatedString
and their modification is carried out using the functions annotations
and annotate!
.
Stylization using AnnotatedString
Fonts
The Face
type
'Face' defines the details of the font that the text can be typed in. It covers a set of basic attributes that are well suited for various formats, namely:
-
font
-
height
-
weight
-
slant
-
foreground
-
background
-
underline
-
strikethrough
-
inverse
-
inherit
Global Font Dictionary
For the convenience of references to certain styles, there is a global dictionary Dict'.{Symbol, Face}
, which allows you to link to fonts (Face
) just by name. Packages can add fonts to this dictionary using the function addface!
, and downloaded fonts can be easily customize.
!!! warning "Appropriate face naming"
Any package registering new fonts must ensure that they have a prefix in the form of the package name, i.e. compliance with the format mypackage_myface
. This is important for predictability and avoiding naming conflicts.
Additionally, packages should use (and introduce) *semantic* fonts (e.g., `code`) rather than direct colors and styles (e.g., `cyan`). This is useful for a variety of reasons, from a more obvious intent in use, to compositionality and more intuitive user customization.
There are two exceptions to the packet prefix rule:
-
a set of basic fonts that are part of the default value of the font dictionary;
-
fonts provided by Julia’s own standard library, namely `JuliaSyntaxHighlighting'.
Basic fonts
The basic fonts are designed to represent a broadly applicable general idea.
To set text with a specific attribute, there are the following fonts: bold
, light
, italic
, underline
, strikethrough
and `inverse'.
Named fonts are also provided 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
and `bright_white'.
For shaded text (i.e., dim but present), the shadow
font is used. There is a region
font to indicate the selected area. The fonts emphasis
and highlight
are defined to indicate significance and emphasis. There is also a `code' for code-like text.
Fonts such as error
, warning
, success
, info
, note
and tip
are defined to visually indicate the severity of messages.
Font Settings (`Faces.toml')
It is advisable that the named fonts in the global font dictionary are customizable. The design and aesthetics are a wonderful addition, which is also important in terms of special features. The TOML file can be analyzed into a list of specifications. Face
, which are combined with an existing entry in the font dictionary.
Font (Face
) is represented in TOML as follows:
[facename]
attribute = "value"
...
[package.facename]
attribute = "value"
For example, if the font shadow
is too difficult to read, you can make it brighter as follows:
[shadow]
foreground = "white"
During initialization, the file config/faces.toml
is loaded in the first Julia repository (usually ~/.julia
).
Applying fonts to the 'AnnotatedString`
Traditional attributes :face
AnnotatedString
contains information about fonts (Face
), which are currently in use. They can be specified in several forms: as a single character (Symbol
) naming fonts (Face
) in the global dictionary of fonts, the font itself (Face
) or a vector of any of them.
The methods show(::IO, ::MIME"text/plain", ::AnnotatedString)
and show(::IO, ::MIME"text/html", ::AnnotatedString)
examine attributes`:face` and combine them when defining a common style.
You can specify attributes :face
for `AnnotatedString' during construction, add them to the list of properties or use convenient literals of stylized strings.
str1 = AnnotatedString("blue text", [(1:9, :face, :blue)])
str2 = styled"{blue:blue text}"
str1 == str2
sprint(print, str1, context = :color => true)
sprint(show, MIME("text/html"), str1, context = :color => true)
Literals of stylized strings
To simplify the creation AnnotatedString
with applied fonts (Face
), a literal of stylized strings styled"..."
makes it easy to express content and attributes using a custom grammar.
Inside the literal styled"..."
curly braces are considered special characters and must be escaped in normal use (\{
, \}
). This allows them to be used to express annotations with (nested) constructs {annotations...:text}
.
The annotations' component...
is a comma-separated list of three types of annotations.
-
Font Names
-
Expressions
+(key=val,…)+`Embedded `Face
-
key=value
pairs
Interpolation is possible everywhere except for embedded font keys.
For more information about the grammar, see the extended help for docstring. styled"..."
.
As an example, you can demonstrate the list of embedded fonts mentioned above as follows:
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) can
arbitrarily nest and overlap, {region,tip:like {bold,italic:so}}.")
Documenter doesn't properly represent all the styling above, so I've converted it manually to HTML and LaTeX.
<pre>
The basic font-style attributes are <span style="font-weight: 700;">bold, <span style="font-weight: 300;">light, <span style="font-style: italic;">italic,
<span style="text-decoration: underline;">underline, and <span style="text-decoration: line-through">strikethrough.
In terms of color, we have named faces for the 16 standard terminal colors:
<span style="color: #1c1a23;">■ <span style="color: #a51c2c;">■ <span style="color: #25a268;">■ <span style="color: #e5a509;">■ <span style="color: #195eb3;">■ <span style="color: #803d9b;">■ <span style="color: #0097a7;">■ <span style="color: #dddcd9;">■
<span style="color: #76757a;">■ <span style="color: #ed333b;">■ <span style="color: #33d079;">■ <span style="color: #f6d22c;">■ <span style="color: #3583e4;">■ <span style="color: #bf60ca;">■ <span style="color: #26c6da;">■ <span style="color: #f6f5f4;">■
Since <span style="color: #0097a7;">bright_black является фактически серым, мы определяем для него два псевдонима:
<span style="color: #0097a7;">grey и <span style="color: #0097a7;">grey для учета различий в написании.
To flip the foreground and background colors of some text, you can use the
<span style="color: #0097a7;">inverse, например: <span style="color: #803d9b;">какой-то <span style="background-color: #803d9b;">обратный<span style="color: #803d9b;"> текст.
The intent-based basic faces are <span style="color: #76757a;">shadow (для неяркого, но видимого текста),
<span style="background-color: #3a3a3a;">region для выделений, <span style="color: #195eb3;">emphasis и <span style="background-color: #195eb3;">highlight.
As above, <span style="color: #0097a7;">code используется для текста, похожего на код.
Lastly, we have the 'message severity' faces: <span style="color: #ed333b;">error, <span style="color: #e5a509;">warning,
<span style="color: #25a268;">success, <span style="color: #26c6da;">info, <span style="color: #76757a;">note и <span style="color: #33d079;">tip.
Remember that all these faces (and any user or package-defined ones) can
arbitrarily nest and overlap, <span style="color: #33d079;background-color: #3a3a3a;">таким <span style="font-weight: 700;font-style: italic;">образом.</pre>
\begingroup
\ttfamily
\setlength{\parindent}{0pt}
\setlength{\parskip}{\baselineskip}
The basic font-style attributes are {\fontseries{b}\selectfont bold}, {\fontseries{l}\selectfont light}, {\fontshape{it}\selectfont italic},\\
\underline{underline}, and {strikethrough}.
In terms of color, we have named faces for the 16 standard terminal colors:\\
{\color[HTML]{1c1a23}\(\blacksquare\)} {\color[HTML]{a51c2c}\(\blacksquare\)} {\color[HTML]{25a268}\(\blacksquare\)}
{\color[HTML]{e5a509}\(\blacksquare\)} {\color[HTML]{195eb3}\(\blacksquare\)} {\color[HTML]{803d9b}\(\blacksquare\)}
{\color[HTML]{0097a7}\(\blacksquare\)} {\color[HTML]{dddcd9}\(\blacksquare\)} \\
{\color[HTML]{76757a}\(\blacksquare\)} {\color[HTML]{ed333b}\(\blacksquare\)} {\color[HTML]{33d079}\(\blacksquare\)} {\color[HTML]{f6d22c}\(\blacksquare\)} {\color[HTML]{3583e4}\(\blacksquare\)} {\color[HTML]{bf60ca}\(\blacksquare\)} {\color[HTML]{26c6da}\(\blacksquare\)} {\color[HTML]{f6f5f4}\(\blacksquare\)}
Since {\color[HTML]{0097a7}bright\_black} is effectively grey, we define two aliases for it:\\
{\color[HTML]{0097a7}grey} and {\color[HTML]{0097a7}gray} to allow for regional spelling differences.
To flip the foreground and background colors of some text, you can use the\\
{\color[HTML]{0097a7}inverse} face, for example: {\color[HTML]{803d9b}some \colorbox[HTML]{803d9b}{\color[HTML]{000000}inverse} text}.
The intent-based basic faces are {\color[HTML]{76757a}shadow} (for dim but visible text),\\
\colorbox[HTML]{3a3a3a}{region} for selections, {\color[HTML]{195eb3}emphasis}, and \colorbox[HTML]{195eb3}{highlight}.\\
As above, {\color[HTML]{0097a7}code} is used for code-like text.
Lastly, we have the 'message severity' faces: {\color[HTML]{ed333b}error}, {\color[HTML]{e5a509}warning},\\
{\color[HTML]{25a268}success}, {\color[HTML]{26c6da}info}, {\color[HTML]{76757a}note}, and {\color[HTML]{33d079}tip}.
Remember that all these faces (and any user or package-defined ones) can\\
arbitrarily nest and overlap, \colorbox[HTML]{3a3a3a}{\color[HTML]{33d079}like
{\fontseries{b}\fontshape{it}\selectfont so}}.
\endgroup
API Help
Styles and fonts
#
StyledStrings.StyledMarkup.@styled_str
— Macro
@styled_str -> AnnotatedString
Creates a stylized string. Inside the structure string {<specs>:<content>}
apply formatting to <content>
according to a comma-separated list of specifications <specs>'. Each specification can take the form of a font name, an embedded font specification, or a pair of `key=value'. If the value contains the characters `,=:{}
, оно должно быть заключено в {...}
.
String interpolation using $
works the same way as regular strings, except that quotation marks need to be escaped. Fonts, keys, and values can also be interpolated using $
.
Example
styled"The {bold:{italic:quick} {(foreground=#cd853f):brown} fox} jumped over the {link={https://en.wikipedia.org/wiki/Laziness}:lazy} dog"
Advanced 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 additional condition not specified in the grammar above is that plain
must be valid input data for unescape_string
, while the specialchar
is saved.
The grammar above for inlineface
is simplified, the actual implementation is a bit more complicated. The full description of the behavior 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
— Function
styled(content::AbstractString) -> AnnotatedString
Creates a stylized string. Inside the structure string {<specs>:<content>}
apply formatting to <content>
according to a comma-separated list of specifications <specs>'. Each specification can take the form of a font name, an embedded font specification, or a pair of `key=value'. If the value contains the characters `,=:{}
, оно должно быть заключено в {...}
.
This is the functional equivalent of a macro. @styled_str
, only without the possibility of interpolation.
#
StyledStrings.Face
— Type
Face
is a collection of graphic attributes for displaying text. Fonts control how text is displayed in the terminal, and possibly in other places as well.
Most of the time Face
will be stored in global font dictionaries as a unique link to the font_name symbol, and most often it will be accessed by that name rather than the object itself. Face
.
Attributes
All attributes can be set using the named argument constructor; by default, they are set to `nothing'.
-
height
(Int
orFloat64
): height either in decimal points (Int
) or as a multiplier of the base size (Float64
). -
weight
(Symbol
): one of the symbols (from the faintest to the most saturated):thin
,:extralight
,:light
,:semilight
,:normal
,:medium
,:semibold
,:bold
,:extrabold
or:black
. In terminals with any saturation greater than:normal
, the text is displayed in bold, and in terminals that support text of variable brightness, with any saturation less than:normal
, the text is displayed in dim font. -
slant
(Symbol
): one of the characters:italic
,:oblique
or `:normal'. -
'foreground' (`SimpleColor'): the foreground color of the text.
-
background
(`SimpleColor'): the background color of the text. -
`underline', underlining text in one of the following forms:
-
Bool
: whether the text should be underlined. -
SimpleColor
: the text should be underlined in this color. -
Tuple{Nothing, Symbol}
: the text must be underlined using the style specified by the character:straight
,:double
,:curly
,:dotted
or `:dashed'. -
Tuple{SimpleColor, Symbol}
: The text must be underlined in the specified SimpleColor color and using the style specified by the symbol, as before.
-
-
strikethrough' (`Bool
): whether the text should be crossed out. -
inverse
(Bool
): Whether the foreground and background colors should be inverted. -
inherit
(Vector{Symbol}
): names of fonts from which inheritance should take place, with earlier fonts taking precedence. All fonts are inherited from the font `:default'.
#
StyledStrings.addface!
— Function
addface!(name::Symbol => default::Face)
Creates a new font named name'. If there are no fonts with that name yet, `default
is added to both FACES
.default` and (a copy of) FACES
.`current', and the current value is returned.
If the font name
already exists, nothing
is returned.
Examples
julia> addface!(:mypkg_myface => Face(slant=:italic, underline=true))
Face (sample)
slant: italic
underline: true
#
StyledStrings.withfaces
— Function
withfaces(f, kv::Pair...)
withfaces(f, kvpair_itr)
Executes f
with FACES
.current`, temporarily changed to zero or more arguments :name => val
kv
, or kvpair_itr
, which creates values in the form `kv'.
The 'within` function is usually used with the syntax within(kv...) do ... end
. Using the nothing
value, you can temporarily disable the font (if it has been set). When the 'withfaces` function returns control, the original FACES'.current
is restored.
Examples
julia> withfaces(:yellow => Face(foreground=:red), :green => :blue) do
println(styled"{yellow:red} and {green:blue} mixed make {magenta:purple}")
end
red and blue mixed make purple
#
StyledStrings.SimpleColor
— Type
struct SimpleColor
A basic color representation designed for string styling. It can contain either a named color (for example, :red
), or an object RGBTuple
, which is a tuple of NamedTuple defining the color r
, g
, b
with a depth of 8 bits.
Constructors
SimpleColor(name::Symbol) # например, :red
SimpleColor(rgb::RGBTuple) # например, (r=1, b=2, g=3)
SimpleColor(r::Integer, b::Integer, b::Integer)
SimpleColor(rgb::UInt32) # например, 0x123456
See also the description of tryparse(SimpleColor, rgb::String)
.
#
Base.parse
— Method
parse(::Type{SimpleColor}, rgb::String)
An analog of tryparse(SimpleColor, rgb::String)
, which returns an error instead of returning nothing
.
#
Base.tryparse
— Method
tryparse(::Type{SimpleColor}, rgb::String)
Tries to analyze rgb
as SimpleColor'. If the `rgb
value starts with #
and has a length of 7, it is converted to SimpleColor
based on RGBTuple'. If the value of `rgb
starts with a
--z
, rgb
is interpreted as the name of the color and converted to SimpleColor
based on the `Symbol'.
Otherwise, `nothing' is returned.
Examples
julia> tryparse(SimpleColor, "blue")
SimpleColor(blue)
julia> tryparse(SimpleColor, "#9558b2")
SimpleColor(#9558b2)
julia> tryparse(SimpleColor, "#nocolor")
#
Base.merge
— Method
merge(initial::Face, others::Face...)
Combines the properties of the font initial
and others
, with the latter taking precedence.