StyledStrings

Note

StyledStrings 和 AnnotatedStrings 的 API 被视为实验性,并可能在 Julia 版本之间发生变化。

Styling

在处理字符串时,格式化和样式通常被视为次要问题。

例如,当打印到终端时,您可能想在输出中添加 ANSI escape sequences,在输出 HTML 样式构造(<span style="..."> 等)时也有类似的目的,等等。可以简单地将原始样式构造插入到内容本身旁边的字符串中,但很快就会发现这并不适合除最基本用例之外的任何情况。并非所有终端都支持相同的 ANSI 代码,在计算已样式内容的宽度时需要小心翼翼地移除样式构造,而这还不包括处理多种输出格式。

为了不让这个头痛的问题在下游被广泛体验,它通过引入一种特殊的字符串类型(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 的注释可以通过 annotationsannotate! 函数进行访问和修改。

Styling via AnnotatedStrings

Faces

The Face type

一个 Face 指定了文本可以设置的字体的详细信息。它涵盖了一组基本属性,这些属性在不同格式之间具有良好的通用性,即:

  • 字体
  • 高度
  • 体重
  • 倾斜
  • 前景
  • 背景
  • 下划线
  • 删除线
  • 反向
  • 继承

有关这些属性具体形式的详细信息,请参见 Face 文档字符串,但特别值得关注的是 inherit,因为它允许您 继承 来自其他 4d61726b646f776e2e436f64652822222c2022466163652229_40726566205374796c6564537472696e67732e46616365 的属性。

The global faces dictionary

为了方便引用特定样式,存在一个全局的 Dict{Symbol, Face},允许通过名称简单地引用 Face。包可以通过 addface! 函数将面添加到此字典中,加载的面可以轻松 customized

Appropriate face naming

任何注册新面孔的包都应确保它们以包名为前缀,即遵循格式 mypackage_myface。这对于可预测性很重要,并且可以防止名称冲突。

此外,软件包应注意使用(并引入)语义面孔(如 code)而不是直接的颜色和样式(如 cyan)。这在多个方面都是有帮助的,从使使用意图更加明显、帮助组合性到使用户自定义更加直观。

有两组对包前缀规则的豁免:

  • 默认值的面字典中包含的基本面集合
  • 由Julia自身的标准库引入的面孔,即JuliaSyntaxHighlighting

Basic faces

基本面孔旨在代表一个广泛适用的一般概念。

对于设置某些具有特定属性的文本,我们有 boldlightitalicunderlinestrikethroughinverse 字体。

还有16种终端颜色的命名面:blackredgreenyellowbluemagentacyanwhitebright_black/grey/graybright_redbright_greenbright_bluebright_magentabright_cyanbright_white

对于阴影文本(即暗淡但仍然可见),有 shadow 字体。为了指示选定区域,有 region 字体。类似地,强调和高亮的字体分别定义为 emphasishighlight。还有 code 用于代码样式的文本。

为了直观地指示消息的严重性,定义了 errorwarningsuccessinfonotetip 面孔。

Customisation of faces (Faces.toml)

在全球面孔字典中,名称面孔可自定义是好的。主题和美学很不错,这对可访问性也很重要。TOML 文件可以解析为一个 Face 规范的列表,这些规范与面孔字典中现有条目合并。

一个 Face 在 TOML 中表示如下:

[facename]
attribute = "value"
...

[package.facename]
attribute = "value"

例如,如果 shadow 面太难以阅读,可以像这样使其变得更亮:

[shadow]
foreground = "white"

在初始化时,首个 Julia 仓库下的 config/faces.toml 文件(通常是 ~/.julia)会被加载。

Applying faces to a AnnotatedString

根据约定,AnnotatedString:face 属性包含当前适用的 Face 的信息。这可以以多种形式给出,作为命名 4d61726b646f776e2e436f64652822222c2022466163652229_40726566205374796c6564537472696e67732e46616365 的单个 Symbol,作为 4d61726b646f776e2e436f64652822222c2022466163652229_40726566205374796c6564537472696e67732e46616365 本身,或作为两者的向量。

show(::IO, ::MIME"text/plain", ::AnnotatedString)show(::IO, ::MIME"text/html", ::AnnotatedString) 方法在确定整体样式时都会查看 :face 属性并将它们合并在一起。

我们可以在构造 AnnotatedString 时提供 :face 属性,之后将它们添加到属性列表中,或者使用方便的 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 的构建,应用 Facestyled"..." 风格的字符串字面量允许通过自定义语法轻松地将内容和属性一起表达。

在一个 styled"..." 字面量中,大括号被视为特殊字符,在正常使用中必须转义(\{\})。这使得它们可以用于表达带有(可嵌套){annotations...:text} 结构的注释。

annotations... 组件是三种类型注释的逗号分隔列表。

  • 面孔名称
  • 行内 Face 表达式 (key=val,...)
  • 键=值

插值在所有地方都是可能的,除了内联面部关键帧。

有关语法的更多信息,请参阅 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>} 结构根据以逗号分隔的规格 <specs> 列表将格式应用于 <content>。每个规格可以是面名称、内联面规格或 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>} 结构根据以逗号分隔的规格 <specs> 列表对 <content> 应用格式。每个规格可以是字体名称、内联字体规格或 key=value 对。若值包含任何字符 ,=:{},则必须用 {...} 包裹。

这是 @styled_str 宏的功能等价物,只是没有插值功能。

source
StyledStrings.FaceType

一个 Face 是用于显示文本的图形属性的集合。Faces 控制文本在终端以及可能的其他地方的显示方式。

大多数情况下,一个 Face 将作为与 face name 符号的唯一关联存储在全局 faces 字典中,并且通常会通过这个名称而不是 Face 对象本身来引用。

属性

所有属性都可以通过关键字构造函数设置,默认值为 nothing

  • height(一个 IntFloat64):高度,以十分之一磅(当为 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}):要继承的 faces 名称,较早的 faces 优先。所有 faces 都继承自 :default face。

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,或生成 kv 形式值的 kvpair_itr

withfaces 通常通过 withfaces(kv...) do ... end 语法使用。可以使用 nothing 的值来临时取消设置一个面(如果它已经被设置)。当 withfaces 返回时,原始的 FACES.current 已被恢复。

示例

julia> withfaces(:yellow => Face(foreground=:red), :green => :blue) do
           println(styled"{yellow:red} 和 {green:blue} 混合成 {magenta:purple}")
       end
red 和 blue 混合成 purple
source
StyledStrings.SimpleColorType
struct SimpleColor

颜色的基本表示,旨在用于字符串样式。它可以包含一个命名颜色(如 :red),或一个 RGBTuple,这是一个指定 rgb 颜色的 NamedTuple,位深为 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。如果 rgba-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 面和 others 的属性,后面的面具有优先权。

source