StyledStrings

Note

La API para StyledStrings y AnnotatedStrings se considera experimental y está sujeta a cambios entre versiones de Julia.

Styling

Cuando se trabaja con cadenas, el formato y el estilo a menudo aparecen como una preocupación secundaria.

Por ejemplo, al imprimir en un terminal, es posible que desees esparcir ANSI escape sequences en la salida. Al generar construcciones de estilo HTML (<span style="...">, etc.), se cumple un propósito similar, y así sucesivamente. Es posible simplemente insertar las construcciones de estilo en bruto en la cadena junto al contenido mismo, pero rápidamente se hace evidente que esto no es adecuado para nada más que los casos de uso más básicos. No todos los terminales admiten los mismos códigos ANSI, las construcciones de estilo deben ser meticulosamente eliminadas al calcular el ancho del contenido ya estilizado, y eso es antes de que incluso comiences a manejar múltiples formatos de salida.

En lugar de dejar que este dolor de cabeza sea ampliamente experimentado en la parte inferior, se aborda de manera directa mediante la introducción de un tipo de cadena especial (AnnotatedString). Este tipo de cadena envuelve cualquier otro tipo AbstractString y permite que se apliquen información de formato a regiones (por ejemplo, los caracteres del 1 al 7 son negrita y rojos).

Las regiones de una cadena se estilizan aplicando Face (piensa en "tipografía") a ellas: una estructura que contiene información de estilo. Como conveniencia, las tipografías en el diccionario global de tipografías (por ejemplo, shadow) pueden ser nombradas en lugar de dar el 4d61726b646f776e2e436f64652822222c2022466163652229_40726566205374796c6564537472696e67732e46616365 directamente.

Junto con estas capacidades, también proporcionamos una forma conveniente de construir AnnotatedString, detallado en Styled String Literals.

julia> using StyledStrings
julia> styled"{yellow:hello} {blue:there}""hello there"

Annotated Strings

A veces es útil poder mantener metadatos relacionados con regiones de una cadena. Un AnnotatedString envuelve otra cadena y permite que las regiones de esta sean anotadas con valores etiquetados (:label => value). Todas las operaciones de cadena genéricas se aplican a la cadena subyacente. Sin embargo, cuando es posible, se preserva la información de estilo. Esto significa que puedes manipular un 4d61726b646f776e2e436f64652822222c2022416e6e6f7461746564537472696e672229_4072656620426173652e416e6e6f7461746564537472696e67 —tomando subcadenas, rellenándolas, concatenándolas con otras cadenas— y las anotaciones de metadatos "vendrán junto con el viaje".

Este tipo de cadena es fundamental para el StyledStrings stdlib, que utiliza anotaciones etiquetadas con :face para mantener información de estilo.

Al concatenar un AnnotatedString, asegúrate de usar annotatedstring en lugar de string si deseas mantener las anotaciones de cadena.

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

Las anotaciones de un AnnotatedString se pueden acceder y modificar a través de las funciones annotations y annotate!.

Styling via AnnotatedStrings

Faces

The Face type

Un Face especifica detalles de una tipografía en la que se puede establecer texto. Cubre un conjunto de atributos básicos que se generalizan bien en diferentes formatos, a saber:

  • fuente
  • altura
  • peso
  • inclinación
  • primer plano
  • fondo
  • subrayar
  • tachado
  • inverso
  • heredar

Para obtener detalles sobre las formas particulares que toman estos atributos, consulta el Face docstring, pero de particular interés es inherit, ya que te permite heredar atributos de otros 4d61726b646f776e2e436f64652822222c2022466163652229_40726566205374796c6564537472696e67732e46616365s.

The global faces dictionary

Para hacer que la referencia a estilos particulares sea más conveniente, hay un Dict{Symbol, Face} global que permite que los Face se refieran simplemente por nombre. Los paquetes pueden agregar caras a este diccionario a través de la función addface!, y las caras cargadas pueden ser fácilmente customized.

Appropriate face naming

Cualquier paquete que registre nuevas caras debe asegurarse de que estén precedidas por el nombre del paquete, es decir, seguir el formato mypackage_myface. Esto es importante para la previsibilidad y para prevenir conflictos de nombres.

Además, los paquetes deben tener cuidado de usar (e introducir) caras semánticas (como code) en lugar de colores y estilos directos (como cyan). Esto es útil de varias maneras, desde hacer que la intención en el uso sea más obvia, ayudar a la composabilidad y hacer que la personalización del usuario sea más intuitiva.

Hay dos conjuntos de exenciones a la regla del prefijo del paquete:

  • el conjunto de caras básicas que son parte del valor predeterminado del diccionario de caras
  • caras introducidas por la propia biblioteca estándar de Julia, a saber, JuliaSyntaxHighlighting

Basic faces

Las caras básicas están destinadas a representar una idea general que es ampliamente aplicable.

Para establecer un texto con un cierto atributo, tenemos las caras negrita, ligera, cursiva, subrayado, tachado e inversa.

También hay nombres para los 16 colores terminales: negro, rojo, verde, amarillo, azul, magenta, cian, blanco, negro_brillante/gris, rojo_brillante, verde_brillante, azul_brillante, magenta_brillante, cian_brillante y blanco_brillante.

Para el texto sombreado (es decir, tenue pero presente) existe la cara shadow. Para indicar una región seleccionada, está la cara region. De manera similar, para énfasis y resaltado se definen las caras emphasis y highlight. También hay code para texto similar al código.

Para indicar visualmente la gravedad de los mensajes, se definen las caras de error, warning, success, info, note y tip.

Customisation of faces (Faces.toml)

Es bueno que los nombres de las caras en el diccionario global de caras sean personalizables. La tematización y la estética son agradables, y también es importante por razones de accesibilidad. Un archivo TOML se puede analizar en una lista de Face especificaciones que se fusionan con la entrada preexistente en el diccionario de caras.

Un Face se representa en TOML de la siguiente manera:

[facename]
attribute = "value"
...

[package.facename]
attribute = "value"

Por ejemplo, si la cara shadow es demasiado difícil de leer, se puede hacer más brillante así:

[shadow]
foreground = "white"

Al iniciar, se carga el archivo config/faces.toml en el primer depósito de Julia (generalmente ~/.julia).

Applying faces to a AnnotatedString

Por convención, los atributos :face de un AnnotatedString contienen información sobre los Face que actualmente se aplican. Esto se puede dar en múltiples formas, como un único Symbol que nombra un 4d61726b646f776e2e436f64652822222c2022466163652229_40726566205374796c6564537472696e67732e46616365 en el diccionario de caras global, un 4d61726b646f776e2e436f64652822222c2022466163652229_40726566205374796c6564537472696e67732e46616365 en sí, o un vector de cualquiera de ellos.

Los métodos show(::IO, ::MIME"text/plain", ::AnnotatedString) y show(::IO, ::MIME"text/html", ::AnnotatedString) ambos examinan los atributos :face y los combinan todos al determinar el estilo general.

Podemos suministrar atributos :face a un AnnotatedString durante la construcción, agregarlos a la lista de propiedades después, o usar el conveniente 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

Para facilitar la construcción de AnnotatedStrings con Faces aplicados, el literal de cadena estilizado styled"..." permite que el contenido y los atributos se expresen fácilmente juntos a través de una gramática personalizada.

Dentro de un literal styled"...", las llaves se consideran caracteres especiales y deben ser escapadas en el uso normal (\{, \}). Esto permite que se utilicen para expresar anotaciones con construcciones {anotaciones...:texto} (anidables).

El componente annotations... es una lista separada por comas de tres tipos de anotaciones.

  • Nombres de cara
  • Expresiones Face en línea (clave=valor,...)
  • clave=valor pares

La interpolación es posible en todas partes excepto para las claves de cara en línea.

Para más información sobre la gramática, consulte la ayuda extendida del docstring styled"...".

Como ejemplo, podemos demostrar la lista de caras integradas mencionadas anteriormente de la siguiente manera:

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

Construya una cadena con estilo. Dentro de la cadena, las estructuras {<specs>:<content>} aplican el formato a <content>, de acuerdo con la lista de especificaciones separadas por comas <specs>. Cada especificación puede tomar la forma de un nombre de cara, una especificación de cara en línea, o un par key=value. El valor debe estar envuelto por {...} si contiene alguno de los caracteres ,=:{}.

La interpolación de cadenas con $ funciona de la misma manera que las cadenas regulares, excepto que las comillas deben ser escapadas. Las caras, claves y valores también se pueden interpolar con $.

Ejemplo

styled"The {bold:{italic:quick} {(foreground=#cd853f):brown} fox} jumped over the {link={https://en.wikipedia.org/wiki/Laziness}:lazy} dog"

Ayuda extendida

Este macro se puede describir mediante la siguiente gramática 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' } ; (* espacio en blanco *)

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 = [^${},:], [^,:]* ;

Una estipulación adicional no codificada en la gramática anterior es que plain debe ser una entrada válida para unescape_string, con specialchar mantenido.

La gramática anterior para inlineface está simplificada, ya que la implementación real es un poco más sofisticada. El comportamiento completo se da a continuación.

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

Construye una cadena estilizada. Dentro de la cadena, las estructuras {<specs>:<content>} aplican el formato a <content>, de acuerdo con la lista de especificaciones separadas por comas <specs>. Cada especificación puede tomar la forma de un nombre de cara, una especificación de cara en línea, o un par key=value. El valor debe estar envuelto por {...} si contiene alguno de los caracteres ,=:{}.

Esta es una equivalente funcional de la macro @styled_str, solo que sin capacidades de interpolación.

source
StyledStrings.FaceType

Una Face es una colección de atributos gráficos para mostrar texto. Las caras controlan cómo se muestra el texto en la terminal, y posiblemente en otros lugares también.

La mayoría de las veces, una Face se almacenará en los diccionarios globales de caras como una asociación única con un nombre de cara Symbol, y se referirá a menudo por este nombre en lugar del objeto Face en sí.

Atributos

Todos los atributos se pueden establecer a través del constructor de palabras clave y por defecto son nothing.

  • height (un Int o Float64): La altura en deci-pt (cuando es un Int), o como un factor del tamaño base (cuando es un Float64).

  • weight (un Symbol): Uno de los símbolos (de más tenue a más denso) :thin, :extralight, :light, :semilight, :normal, :medium, :semibold, :bold, :extrabold, o :black. En las terminales, cualquier peso mayor que :normal se muestra como negrita, y en las terminales que soportan texto de brillo variable, cualquier peso menor que :normal se muestra como tenue.

  • slant (un Symbol): Uno de los símbolos :italic, :oblique, o :normal.

  • foreground (un SimpleColor): El color del primer plano del texto.

  • background (un SimpleColor): El color de fondo del texto.

  • underline, el subrayado del texto, que toma una de las siguientes formas:

    • un Bool: Si el texto debe estar subrayado o no.
    • un SimpleColor: El texto debe estar subrayado con este color.
    • un Tuple{Nothing, Symbol}: El texto debe estar subrayado utilizando el estilo establecido por el Symbol, uno de :straight, :double, :curly, :dotted, o :dashed.
    • un Tuple{SimpleColor, Symbol}: El texto debe estar subrayado en el SimpleColor especificado, y utilizando el estilo especificado por el Symbol, como antes.
  • strikethrough (un Bool): Si el texto debe estar tachado.

  • inverse (un Bool): Si los colores de primer plano y fondo deben ser invertidos.

  • inherit (un Vector{Symbol}): Nombres de caras de las que heredar, con las caras anteriores tomando prioridad. Todas las caras heredan de la cara :default.

source
StyledStrings.addface!Function
addface!(name::Symbol => default::Face)

Crea una nueva cara con el nombre name. Siempre que no exista ya una cara con este nombre, default se añade tanto a FACES.default como (una copia de) a FACES.current, con el valor actual devuelto.

Si la cara name ya existe, se devuelve nothing.

Ejemplos

julia> addface!(:mypkg_myface => Face(slant=:italic, underline=true))
Face (muestra)
         slant: italic
     underline: true
source
StyledStrings.withfacesFunction
withfaces(f, kv::Pair...)
withfaces(f, kvpair_itr)

Ejecuta f con FACES.current temporalmente modificado por cero o más argumentos :name => val kv, o kvpair_itr que produce valores en forma de kv.

withfaces se utiliza generalmente a través de la sintaxis withfaces(kv...) do ... end. Un valor de nothing se puede usar para desactivar temporalmente una cara (si ha sido configurada). Cuando withfaces retorna, el original FACES.current ha sido restaurado.

Ejemplos

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

Una representación básica de un color, destinada a propósitos de estilo de cadena. Puede contener un color nombrado (como :red), o un RGBTuple que es un NamedTuple que especifica un color r, g, b con una profundidad de bits de 8.

Constructores

SimpleColor(name::Symbol)  # p. ej. :red
SimpleColor(rgb::RGBTuple) # p. ej. (r=1, b=2, g=3)
SimpleColor(r::Integer, b::Integer, b::Integer)
SimpleColor(rgb::UInt32)   # p. ej. 0x123456

También consulta tryparse(SimpleColor, rgb::String).

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

Un análogo de tryparse(SimpleColor, rgb::String) (ver), que genera un error en lugar de devolver nothing.

source
Base.tryparseMethod
tryparse(::Type{SimpleColor}, rgb::String)

Intenta analizar rgb como un SimpleColor. Si rgb comienza con # y tiene una longitud de 7, se convierte en un SimpleColor respaldado por un RGBTuple. Si rgb comienza con a-z, rgb se interpreta como un nombre de color y se convierte en un SimpleColor respaldado por un Symbol.

De lo contrario, se devuelve nothing.

Ejemplos

julia> tryparse(SimpleColor, "blue")
SimpleColor(blue)

julia> tryparse(SimpleColor, "#9558b2")
SimpleColor(#9558b2)

julia> tryparse(SimpleColor, "#nocolor")
source
Base.mergeMethod
merge(initial::Face, others::Face...)

Fusiona las propiedades de la cara initial y others, dando prioridad a las caras posteriores.

source