StyledStrings

Note

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 StyledStrings
julia> 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.

Appropriate face naming

Любой пакет, регистрирующий новые лица, должен убедиться, что они начинаются с имени пакета, т.е. следовать формату 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 == str2true
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>"

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_strMacro
@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 ;
source
StyledStrings.StyledMarkup.styledFunction
styled(content::AbstractString) -> AnnotatedString

Создает стилизованную строку. Внутри строки структуры {<specs>:<content>} применяют форматирование к <content>, в соответствии со списком спецификаций <specs>, разделенных запятыми. Каждая спецификация может принимать форму имени шрифта, спецификации встроенного шрифта или пары key=value. Значение должно быть заключено в {...}, если оно содержит любые из символов ,=:{}.

Это функциональный эквивалент макроса @styled_str, просто без возможностей интерполяции.

source
StyledStrings.FaceType

Лицо — это коллекция графических атрибутов для отображения текста. Лица контролируют, как текст отображается в терминале и, возможно, в других местах.

Чаще всего Лицо будет храниться в глобальных словарях лиц как уникальная ассоциация с символом имя лица, и чаще всего будет упоминаться по этому имени, а не по самому объекту Лицо.

Атрибуты

Все атрибуты могут быть установлены через конструктор ключевых слов и по умолчанию равны 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.

source
StyledStrings.addface!Function
addface!(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: true
source
StyledStrings.withfacesFunction
withfaces(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 purple
source
StyledStrings.SimpleColorType
struct 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).

source
Base.parseMethod
parse(::Type{SimpleColor}, rgb::String)

Аналог функции tryparse(SimpleColor, rgb::String) (см. там), которая вызывает ошибку вместо возврата nothing.

source
Base.tryparseMethod
tryparse(::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")
source
Base.mergeMethod
merge(initial::Face, others::Face...)

Объединяет свойства начального лица initial и других, при этом более поздние лица имеют приоритет.

source