StyledStrings
API для StyledStrings и AnnotatedStrings считается экспериментальным и может изменяться между версиями Julia.
Styling
При работе со строками форматирование и стилизация часто выступают в качестве второстепенной задачи.
Например, при печати в терминал вы можете захотеть добавить ANSI escape sequences в вывод, когда выводите конструкции HTML-стилизации (<span style="..."> и т. д.), которые служат аналогичной цели, и так далее. Возможно просто вставить сырые конструкции стилизации в строку рядом с самим содержимым, но быстро становится очевидным, что это не очень подходит для чего-либо, кроме самых базовых случаев использования. Не все терминалы поддерживают одни и те же коды ANSI, конструкции стилизации необходимо тщательно удалять при расчете ширины уже стилизованного контента, и это еще до того, как вы даже начнете обрабатывать несколько форматов вывода.
Вместо того чтобы оставлять эту головную боль для широкого опыта downstream, она решается напрямую с помощью введения специального типа строки (AnnotatedString). Этот тип строки оборачивает любой другой тип AbstractString и позволяет применять информацию о форматировании к регионам (например, символы с 1 по 7 выделены жирным шрифтом и красным цветом).
Регионы строки оформляются применением Face (думайте о "шрифте") к ним — структура, которая содержит информацию о стиле. В качестве удобства, шрифты в глобальном словаре шрифтов (например, shadow) могут просто называться вместо того, чтобы указывать 4d61726b646f776e2e436f64652822222c2022466163652229_40726566205374796c6564537472696e67732e46616365 напрямую.
Вместе с этими возможностями мы также предоставляем удобный способ для построения AnnotatedString, подробно описанный в Styled String Literals.
julia> using StyledStringsjulia> styled"{yellow:hello} {blue:there}""hello there"
Annotated Strings
Иногда полезно иметь возможность хранить метаданные, относящиеся к регионам строки. AnnotatedString оборачивает другую строку и позволяет аннотировать ее регионы с помощью помеченных значений (:label => value). Все общие операции со строками применяются к основной строке. Однако, когда это возможно, информация о стиле сохраняется. Это означает, что вы можете манипулировать 4d61726b646f776e2e436f64652822222c2022416e6e6f7461746564537472696e672229_4072656620426173652e416e6e6f7461746564537472696e67 — брать подстроки, добавлять к ним отступы, конкатенировать их с другими строками — и аннотации метаданных "поедут с вами".
Этот тип строки является фундаментальным для StyledStrings stdlib, который использует аннотации с меткой :face для хранения информации о стиле.
При конкатенации AnnotatedString будьте внимательны и используйте annotatedstring вместо string, если вы хотите сохранить аннотации строк.
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) # *-concatenation works
trueАннотации AnnotatedString могут быть доступны и изменены через функции annotations и annotate!.
Styling via AnnotatedStrings
Faces
The Face type
Face задает детали шрифта, в котором может быть установлен текст. Он охватывает набор основных атрибутов, которые хорошо обобщаются на различных форматах, а именно:
шрифтвысотавесскосforegroundфонподчеркнутьзачеркнутыйобратныйнаследовать
Для получения подробной информации о конкретных формах, которые принимают эти атрибуты, смотрите Face docstring, но особый интерес представляет inherit, так как он позволяет вам наследовать атрибуты от других 4d61726b646f776e2e436f64652822222c2022466163652229_40726566205374796c6564537472696e67732e46616365.
The global faces dictionary
Чтобы сделать ссылку на определенные стили более удобной, существует глобальный Dict{Symbol, Face}, который позволяет ссылаться на Face просто по имени. Пакеты могут добавлять шрифты в этот словарь с помощью функции addface!, а загруженные шрифты можно легко customized.
Любой пакет, регистрирующий новые лица, должен убедиться, что они начинаются с имени пакета, т.е. следовать формату mypackage_myface. Это важно для предсказуемости и предотвращения конфликтов имен.
Кроме того, пакеты должны заботиться о том, чтобы использовать (и вводить) семантические лица (например, code) вместо прямых цветов и стилей (например, cyan). Это полезно во многих отношениях: от того, что делает намерение в использовании более очевидным, до помощи в композируемости и упрощения пользовательской настройки.
Существует два набора исключений из правила префикса пакета:
- набор основных лиц, которые являются частью значения по умолчанию словаря лиц
- лица, введенные стандартной библиотекой Julia, а именно
JuliaSyntaxHighlighting
Basic faces
Основные лица предназначены для представления общей идеи, которая широко применима.
Для установки некоторого текста с определенным атрибутом у нас есть шрифты bold, light, italic, underline, strikethrough и inverse.
Существуют также именованные цвета для 16 терминальных цветов: black, red, green, yellow, blue, magenta, cyan, white, bright_black/grey/gray, bright_red, bright_green, bright_blue, bright_magenta, bright_cyan и bright_white.
Для затененного текста (т.е. тусклого, но присутствующего) существует стиль shadow. Чтобы указать выбранный регион, используется стиль region. Аналогично для акцента и выделения определены стили emphasis и highlight. Также есть стиль code для текста, похожего на код.
Для визуального указания на серьезность сообщений определены лица error, warning, success, info, note и tip.
Customisation of faces (Faces.toml)
Хорошо, что имена лиц в глобальном словаре лиц можно настраивать. Тематика и эстетика важны, и это также важно по причинам доступности. Файл TOML можно разобрать на список спецификаций Face, которые объединяются с уже существующей записью в словаре лиц.
Face представлен в TOML следующим образом:
[facename]
attribute = "value"
...
[package.facename]
attribute = "value"Например, если лицо shadow слишком трудно читать, его можно сделать ярче вот так:
[shadow]
foreground = "white"При инициализации загружается файл config/faces.toml, находящийся в первом хранилище Julia (обычно ~/.julia).
Applying faces to a AnnotatedString
По соглашению, атрибуты :face AnnotatedString содержат информацию о Face, которые в настоящее время применяются. Это может быть представлено в нескольких формах: в виде одного Symbol, указывающего на 4d61726b646f776e2e436f64652822222c2022466163652229_40726566205374796c6564537472696e67732e46616365 в глобальном словаре лиц, самого 4d61726b646f776e2e436f64652822222c2022466163652229_40726566205374796c6564537472696e67732e46616365 или вектором любого из них.
Методы show(::IO, ::MIME"text/plain", ::AnnotatedString) и show(::IO, ::MIME"text/html", ::AnnotatedString) оба рассматривают атрибуты :face и объединяют их все вместе при определении общего стиля.
Мы можем предоставить :face атрибуты для AnnotatedString во время создания, добавить их в список свойств позже или использовать удобный Styled String literals.
julia> str1 = AnnotatedString("blue text", [(1:9, :face, :blue)])"blue text"julia> str2 = styled"{blue:blue text}""blue text"julia> str1 == str2truejulia> 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>"
Styled String Literals
Чтобы упростить создание AnnotatedString с применением Face, строковый литерал в стиле styled"..." позволяет легко выражать содержимое и атрибуты вместе с помощью пользовательской грамматики.
Внутри литерала styled"..." фигурные скобки считаются специальными символами и должны быть экранированы в обычном использовании (\{, \}). Это позволяет использовать их для выражения аннотаций с помощью (вложенных) конструкций {annotations...:text}.
Компонент annotations... представляет собой список из трех типов аннотаций, разделенный запятыми.
- Имена лиц
- Встроенные
Faceвыражения(key=val,...) key=valueпары
Интерполяция возможна везде, кроме ключей лиц в строке.
Для получения дополнительной информации о грамматике смотрите расширенную справку документации styled"...".
В качестве примера мы можем продемонстрировать список встроенных лиц, упомянутых выше, следующим образом:
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}}.")The basic font-style attributes are bold, light, italic, underline, and strikethrough. In terms of color, we have named faces for the 16 standard terminal colors: ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ Since bright_black is effectively grey, we define two aliases for it: grey and gray to allow for regional spelling differences. To flip the foreground and background colors of some text, you can use the inverse face, for example: some inverse text. The intent-based basic faces are shadow (for dim but visible text), region for selections, emphasis, and highlight. As above, code is used for code-like text. Lastly, we have the 'message severity' faces: error, warning, success, info, note, and tip. Remember that all these faces (and any user or package-defined ones) can arbitrarily nest and overlap, like so.
API reference
Styling and Faces
StyledStrings.StyledMarkup.@styled_str — Macro@styled_str -> AnnotatedStringСоздайте стилизованную строку. Внутри строки структуры {<specs>:<content>} применяют форматирование к <content>, в соответствии со списком запятыми, разделенных спецификаций <specs>. Каждая спецификация может принимать форму имени шрифта, спецификации встроенного шрифта или пары key=value. Значение должно быть заключено в {...}, если оно содержит любые из символов ,=:{}.
Интерполяция строк с помощью $ работает так же, как и в обычных строках, за исключением того, что кавычки необходимо экранировать. Шрифты, ключи и значения также могут быть интерполированы с помощью $.
Пример
styled"The {bold:{italic:quick} {(foreground=#cd853f):brown} fox} jumped over the {link={https://en.wikipedia.org/wiki/Laziness}:lazy} dog"Расширенная помощь
Этот макрос можно описать следующей грамматикой EBNF:
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' } ; (* пробелы *)
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 = [^${},:], [^,:]* ;Дополнительное условие, не закодированное в приведенной выше грамматике, заключается в том, что plain должен быть допустимым вводом для unescape_string, с сохранением specialchar.
Приведенная выше грамматика для inlineface упрощена, так как фактическая реализация немного более сложная. Полное поведение описано ниже.
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Создает стилизованную строку. Внутри строки структуры {<specs>:<content>} применяют форматирование к <content>, в соответствии со списком спецификаций <specs>, разделенных запятыми. Каждая спецификация может принимать форму имени шрифта, спецификации встроенного шрифта или пары key=value. Значение должно быть заключено в {...}, если оно содержит любые из символов ,=:{}.
Это функциональный эквивалент макроса @styled_str, просто без возможностей интерполяции.
StyledStrings.Face — TypeЛицо — это коллекция графических атрибутов для отображения текста. Лица контролируют, как текст отображается в терминале и, возможно, в других местах.
Чаще всего Лицо будет храниться в глобальных словарях лиц как уникальная ассоциация с символом имя лица, и чаще всего будет упоминаться по этому имени, а не по самому объекту Лицо.
Атрибуты
Все атрибуты могут быть установлены через конструктор ключевых слов и по умолчанию равны nothing.
height(целое числоIntилиFloat64): Высота в десятых пунктах (когдаInt), или как фактор базового размера (когдаFloat64).weight(символSymbol): Один из символов (от наименее плотного до наиболее плотного):thin,:extralight,:light,:semilight,:normal,:medium,:semibold,:bold,:extraboldили:black. В терминалах любой вес, превышающий:normal, отображается как жирный, а в терминалах, поддерживающих текст с переменной яркостью, любой вес, меньший:normal, отображается как бледный.slant(символSymbol): Один из символов:italic,:obliqueили:normal.foreground(цветSimpleColor): Цвет переднего плана текста.background(цветSimpleColor): Цвет фона текста.underline, подчеркивание текста, которое принимает одну из следующих форм:Bool: Должен ли текст быть подчеркнут или нет.SimpleColor: Текст должен быть подчеркнут этим цветом.Tuple{Nothing, Symbol}: Текст должен быть подчеркнут с использованием стиля, установленного символом, одним из:straight,:double,:curly,:dottedили:dashed.Tuple{SimpleColor, Symbol}: Текст должен быть подчеркнут в указанном цвете SimpleColor и с использованием стиля, указанного символом, как и прежде.
strikethrough(логическое значениеBool): Должен ли текст быть зачеркинут.inverse(логическое значениеBool): Должны ли быть инвертированы цвета переднего плана и фона.inherit(векторVector{Symbol}): Имена лиц, от которых следует наследовать, при этом более ранние лица имеют приоритет. Все лица наследуют от лица:default.
StyledStrings.addface! — Functionaddface!(name::Symbol => default::Face)Создайте новое лицо с именем name. Пока лицо с таким именем еще не существует, default добавляется как в FACES.default, так и (копия) в FACES.current, при этом возвращается текущее значение.
Если лицо name уже существует, возвращается nothing.
Примеры
julia> addface!(:mypkg_myface => Face(slant=:italic, underline=true))
Face (sample)
slant: italic
underline: trueStyledStrings.withfaces — Functionwithfaces(f, kv::Pair...)
withfaces(f, kvpair_itr)Выполните f с временно изменённым FACES.current с помощью нуля или более аргументов :name => val kv, или kvpair_itr, который производит значения в формате kv.
withfaces обычно используется через синтаксис withfaces(kv...) do ... end. Значение nothing может быть использовано для временного сброса лица (если оно было установлено). Когда withfaces возвращается, оригинальный FACES.current восстанавливается.
Примеры
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 purpleStyledStrings.SimpleColor — Typestruct SimpleColorБазовое представление цвета, предназначенное для стилизации строк. Оно может содержать либо именованный цвет (например, :red), либо RGBTuple, который является NamedTuple, указывающим цвет r, g, b с глубиной цвета 8 бит.
Конструкторы
SimpleColor(name::Symbol) # например, :red
SimpleColor(rgb::RGBTuple) # например, (r=1, b=2, g=3)
SimpleColor(r::Integer, b::Integer, b::Integer)
SimpleColor(rgb::UInt32) # например, 0x123456Также смотрите tryparse(SimpleColor, rgb::String).
Base.parse — Methodparse(::Type{SimpleColor}, rgb::String)Аналог функции tryparse(SimpleColor, rgb::String) (см. там), которая вызывает ошибку вместо возврата nothing.
Base.tryparse — Methodtryparse(::Type{SimpleColor}, rgb::String)Попытка разобрать rgb как SimpleColor. Если rgb начинается с # и имеет длину 7, он преобразуется в RGBTuple-поддерживаемый SimpleColor. Если rgb начинается с a-z, rgb интерпретируется как имя цвета и преобразуется в Symbol-поддерживаемый SimpleColor.
В противном случае возвращается nothing.
Примеры
julia> tryparse(SimpleColor, "blue")
SimpleColor(blue)
julia> tryparse(SimpleColor, "#9558b2")
SimpleColor(#9558b2)
julia> tryparse(SimpleColor, "#nocolor")Base.merge — Methodmerge(initial::Face, others::Face...)Объединяет свойства начального лица initial и других, при этом более поздние лица имеют приоритет.