Add S"" as shorthand for styled""

We're about to reach 100 instances of styled"", and at this point I'm
rather keen to avoid typing it out in full each time and save a good few
hundred characters.
This commit is contained in:
TEC 2024-05-01 23:05:16 +08:00
parent 172a33cb1f
commit d4e923c23c
Signed by: tec
SSH Key Fingerprint: SHA256:eobz41Mnm0/iYWBvWThftS0ElEs1ftBr6jamutnXc/A
5 changed files with 84 additions and 82 deletions

View File

@ -5,6 +5,8 @@ using StyledStrings: @styled_str, Face, face!, addface!
using JuliaSyntaxHighlighting: highlight
using InteractiveUtils
const var"@S_str" = var"@styled_str"
export about
include("utils.jl")

View File

@ -3,11 +3,11 @@ function about(io::IO, fn::Function)
methodmodules = getproperty.(methods(fn).ms, :module)
others = setdiff(methodmodules, [source])
fn_name, fn_extra = split(Base.summary(fn), ' ', limit=2)
print(io, styled"{julia_funcall:$fn_name} $fn_extra\n Defined in {about_module:$source}")
print(io, S"{julia_funcall:$fn_name} $fn_extra\n Defined in {about_module:$source}")
if length(others) > 0
print(io, styled"{shadow:({emphasis:$(sum(Ref(source) .=== methodmodules))})} extended in ")
print(io, S"{shadow:({emphasis:$(sum(Ref(source) .=== methodmodules))})} extended in ")
for (i, oth) in enumerate(others)
print(io, styled"{about_module:$oth}{shadow:({emphasis:$(sum(Ref(oth) .=== methodmodules))})}")
print(io, S"{about_module:$oth}{shadow:({emphasis:$(sum(Ref(oth) .=== methodmodules))})}")
if length(others) == 2 && i == 1
print(io, " and ")
elseif length(others) > 2 && i < length(others)-1
@ -21,7 +21,7 @@ function about(io::IO, fn::Function)
end
function about(io::IO, @nospecialize(cfn::ComposedFunction))
print(io, styled"{bold:Composed function:} ")
print(io, S"{bold:Composed function:} ")
fnstack = Function[]
function decompose!(fnstk, c::ComposedFunction)
decompose!(fnstk, c.outer)
@ -29,10 +29,10 @@ function about(io::IO, @nospecialize(cfn::ComposedFunction))
end
decompose!(fnstk, c::Function) = push!(fnstk, c)
decompose!(fnstack, cfn)
join(io, map(f -> styled"{julia_funcall:$f}", fnstack), styled" {julia_operator:∘} ")
join(io, map(f -> S"{julia_funcall:$f}", fnstack), S" {julia_operator:∘} ")
println(io)
for fn in fnstack
print(io, styled" {emphasis:•} ")
print(io, S" {emphasis:•} ")
about(io, fn)
end
end
@ -49,7 +49,7 @@ function about(io::IO, fn::Function, @nospecialize(argtypes::Type{<:Tuple}))
ms = methods(fn, argtypes)
if isempty(ms)
fncall = highlight("$fn($(join(collect(argtypes.types), ", ")))")
println(io, styled" {error:!} No methods matched $fncall")
println(io, S" {error:!} No methods matched $fncall")
return
end
rinfo = let rtypes = Base.return_types(fn, argtypes) # HACK: this is technically private API
@ -64,19 +64,19 @@ function about(io::IO, fn::Function, @nospecialize(argtypes::Type{<:Tuple}))
end
unique!(rtypes)
sort!(rtypes, by=length supertypes)
join(map(t -> styled"{julia_type:$t}", rtypes), ", ")
join(map(t -> S"{julia_type:$t}", rtypes), ", ")
end
println(io, styled" Matched {emphasis:$(length(ms))} method$(ifelse(length(ms) > 1, \"s\", \"\")) {julia_type:::} $rinfo")
println(io, S" Matched {emphasis:$(length(ms))} method$(ifelse(length(ms) > 1, \"s\", \"\")) {julia_type:::} $rinfo")
for method in ms
mcall, msrc = split(sprint(show, method), " @ ")
msrcinfo = match(r"^([A-Z][A-Za-z0-9\.]+) (.+)$", msrc)
msrcpretty = if isnothing(msrcinfo)
styled"{shadow,underline:$msrc}"
S"{shadow,underline:$msrc}"
else
mmod, mfile = msrcinfo.captures
styled"{about_module:$mmod} {shadow,underline:$mfile}"
S"{about_module:$mmod} {shadow,underline:$mfile}"
end
println(io, styled" {light:$(highlight(mcall))} {shadow,bold:@} $msrcpretty")
println(io, S" {light:$(highlight(mcall))} {shadow,bold:@} $msrcpretty")
end
println(io)
@static if VERSION >= v"1.8"
@ -111,7 +111,7 @@ end
else
'~', :warning
end
msg = styled"{bold,italic,grey:???}"
msg = S"{bold,italic,grey:???}"
for (id, label) in labels
if id == value
msg = label
@ -120,51 +120,51 @@ end
end
name_pad_width = 13
dispwidth = last(displaysize(io))
declr = styled" {bold,$accent:$icon $(rpad(name, name_pad_width))} "
declr = S" {bold,$accent:$icon $(rpad(name, name_pad_width))} "
print(io, declr)
indent = name_pad_width + 5
desc = styled"{grey:$prefix$(ifelse(isempty(prefix), \"\", \" \"))$msg$(ifelse(isempty(suffix), \"\", \" \"))$suffix}"
desc = S"{grey:$prefix$(ifelse(isempty(prefix), \"\", \" \"))$msg$(ifelse(isempty(suffix), \"\", \" \"))$suffix}"
desclines = wraplines(desc, dispwidth - indent, indent)
for (i, line) in enumerate(desclines)
i > 1 && print(io, ' '^indent)
println(io, line)
end
end
println(io, styled"{bold:Method effects:}")
println(io, S"{bold:Method effects:}")
effectinfo(io, :consistent, "consistent",
C4.ALWAYS_TRUE => styled"guaranteed to",
C4.ALWAYS_FALSE => styled"might {italic:not}",
C4.CONSISTENT_IF_NOTRETURNED => styled"when the return value never involved newly allocated mutable objects, will",
C4.CONSISTENT_IF_INACCESSIBLEMEMONLY => styled"when {code:inaccessible memory only} is also proven, will",
C4.ALWAYS_TRUE => S"guaranteed to",
C4.ALWAYS_FALSE => S"might {italic:not}",
C4.CONSISTENT_IF_NOTRETURNED => S"when the return value never involved newly allocated mutable objects, will",
C4.CONSISTENT_IF_INACCESSIBLEMEMONLY => S"when {code:inaccessible memory only} is also proven, will",
suffix = "return or terminate consistently")
effectinfo(io, :effect_free, "effect free",
C4.ALWAYS_TRUE => styled"guaranteed to be",
C4.ALWAYS_FALSE => styled"might {italic:not} be",
C4.EFFECT_FREE_IF_INACCESSIBLEMEMONLY => styled"when {code:inaccessible memory only} is also proven, is",
C4.ALWAYS_TRUE => S"guaranteed to be",
C4.ALWAYS_FALSE => S"might {italic:not} be",
C4.EFFECT_FREE_IF_INACCESSIBLEMEMONLY => S"when {code:inaccessible memory only} is also proven, is",
suffix = "free from externally semantically visible side effects")
effectinfo(io, :nothrow, "no throw",
C4.ALWAYS_TRUE => styled"guaranteed to never",
C4.ALWAYS_FALSE => styled"may",
C4.ALWAYS_TRUE => S"guaranteed to never",
C4.ALWAYS_FALSE => S"may",
suffix = "throw an exception")
effectinfo(io, :terminates, "terminates",
C4.ALWAYS_TRUE => styled"guaranteed to",
C4.ALWAYS_FALSE => styled"might {italic:not}",
C4.ALWAYS_TRUE => S"guaranteed to",
C4.ALWAYS_FALSE => S"might {italic:not}",
suffix = "always terminate")
effectinfo(io, :notaskstate, "no task state",
C4.ALWAYS_TRUE => styled"guaranteed not to access task state (allowing migration between tasks)",
C4.ALWAYS_FALSE => styled"may access task state (preventing migration between tasks)")
C4.ALWAYS_TRUE => S"guaranteed not to access task state (allowing migration between tasks)",
C4.ALWAYS_FALSE => S"may access task state (preventing migration between tasks)")
effectinfo(io, :inaccessiblememonly, "inaccessible memory only",
C4.ALWAYS_TRUE => styled"guaranteed to never access or modify externally accessible mutable memory",
C4.ALWAYS_FALSE => styled"may access or modify externally accessible mutable memory",
C4.INACCESSIBLEMEM_OR_ARGMEMONLY => styled"may access or modify mutable memory {italic:iff} pointed to by its call arguments")
C4.ALWAYS_TRUE => S"guaranteed to never access or modify externally accessible mutable memory",
C4.ALWAYS_FALSE => S"may access or modify externally accessible mutable memory",
C4.INACCESSIBLEMEM_OR_ARGMEMONLY => S"may access or modify mutable memory {italic:iff} pointed to by its call arguments")
effectinfo(io, :noub, "no undefined behaviour",
C4.ALWAYS_TRUE => styled"guaranteed to never",
C4.ALWAYS_FALSE => styled"may",
C4.NOUB_IF_NOINBOUNDS => styled"so long as {code,julia_macro:@inbounds} is not used or propagated, will not",
C4.ALWAYS_TRUE => S"guaranteed to never",
C4.ALWAYS_FALSE => S"may",
C4.NOUB_IF_NOINBOUNDS => S"so long as {code,julia_macro:@inbounds} is not used or propagated, will not",
suffix = "execute undefined behaviour")
effectinfo(io, :nonoverlayed, "non-overlayed",
true => styled"never calls any methods from an overlayed method table",
false => styled"{warning:may} call methods from an overlayed method table")
true => S"never calls any methods from an overlayed method table",
false => S"{warning:may} call methods from an overlayed method table")
end
end

View File

@ -43,7 +43,7 @@ function about(io::IO, type::Type)
elseif isconcretetype(type)
print(io, "Concrete ")
if Base.datatype_haspadding(type)
print(io, styled"{shadow:(padded)} ")
print(io, S"{shadow:(padded)} ")
end
elseif isabstracttype(type)
print(io, "Abstract ")
@ -52,22 +52,22 @@ function about(io::IO, type::Type)
print(io, "singleton ")
end
print(Base.summary(type))
print(io, styled" defined in {about_module:$(parentmodule(type))}, ")
print(io, S" defined in {about_module:$(parentmodule(type))}, ")
hassizeof(type) && print(io, "$(join(humansize(sizeof(type))))")
print(io, "\n ")
supertypeinfo(io, type)
(!isstructtype(type) || fieldcount(type) == 0) && return
println(io, styled"\n\nStruct with {bold:$(fieldcount(type))} fields:")
println(io, S"\n\nStruct with {bold:$(fieldcount(type))} fields:")
fieldinfo = AnnotatedString[]
if type isa DataType
sinfo = structinfo(type)
namepad = maximum(fi -> textwidth(string(fi.name)), sinfo) + 1
for (; face, name, type, ispointer) in sinfo
push!(fieldinfo, rpad(styled"{$face:$name}", namepad) * styled"{about_pointer:$(ifelse(ispointer, \"*\", \" \"))}$type")
push!(fieldinfo, rpad(S"{$face:$name}", namepad) * S"{about_pointer:$(ifelse(ispointer, \"*\", \" \"))}$type")
end
else
for (; name, type) in structinfo(type)
push!(fieldinfo, styled"$name{shadow:::$type}")
push!(fieldinfo, S"$name{shadow:::$type}")
end
end
if length(fieldinfo) < 32
@ -83,7 +83,7 @@ end
function supertypeinfo(io::IO, type::Type)
typestr(t) = highlight(sprint(show, Base.unwrap_unionall(t)))
join(io, map(typestr, supertypes(type)),
styled" {julia_comparator:<:} ")
S" {julia_comparator:<:} ")
end
function memorylayout(io::IO, type::DataType)
@ -101,24 +101,24 @@ function memorylayout(io::IO, type::DataType)
width = max(2, memscale * size÷memstep)
fsize, funits = humansize(size)
desc = if ispointer
cpad(styled" {$color,bold:*} ", width)
cpad(S" {$color,bold:*} ", width)
elseif contentsize < size
csize, cunits = humansize(contentsize)
psize, punits = humansize(size - contentsize)
cpad(styled" {$color:$csize$cunits}{shadow:+$psize$punits} ", width, ' ', RoundUp)
cpad(S" {$color:$csize$cunits}{shadow:+$psize$punits} ", width, ' ', RoundUp)
else
cpad(styled" {$color:$fsize$funits} ", width)
cpad(S" {$color:$fsize$funits} ", width)
end
push!(descs, desc)
width = textwidth(desc)
contentwidth = round(Int, width * contentsize / size)
bar = styled"{$color:$('■'^contentwidth)}"
bar = S"{$color:$('■'^contentwidth)}"
if contentsize < size
paddwidth = width - contentwidth
if ispointer
bar *= styled"{about_pointer,light:$('■'^paddwidth)}"
bar *= S"{about_pointer,light:$('■'^paddwidth)}"
else
bar *= styled"{shadow:$('■'^paddwidth)}"
bar *= S"{shadow:$('■'^paddwidth)}"
end
end
push!(bars, bar)
@ -126,6 +126,6 @@ function memorylayout(io::IO, type::DataType)
println(io)
multirow_wrap(io, permutedims(hcat(bars, descs)))
if any(i -> i.ispointer, si)
println(io, styled"\n {about_pointer,bold:*} = {about_pointer:Pointer} {light:(8B)}")
println(io, S"\n {about_pointer,bold:*} = {about_pointer:Pointer} {light:(8B)}")
end
end

View File

@ -40,7 +40,7 @@ end
function columnlist(io::IO, entries::Vector{<:AbstractString};
maxcols::Int=8, maxwidth::Int=last(displaysize(io)),
prefix::AbstractString = styled"{emphasis:•} ", spacing::Int=2)
prefix::AbstractString = S"{emphasis:•} ", spacing::Int=2)
thecolumns = Vector{eltype(entries)}[]
thecolwidths = Int[]
for ncols in 1:maxcols

View File

@ -9,7 +9,7 @@ function about(io::IO, value::T) where {T}
iotype = AnnotatedIOBuffer()
print(iotype, Base.summary(value))
ismutable(value) && print(iotype, " (mutable)")
print(iotype, styled" ({julia_comparator:<:} ")
print(iotype, S" ({julia_comparator:<:} ")
supertypeinfo(iotype, supertype(T))
print(iotype, ")")
infotype = read(seekstart(iotype), AnnotatedString)
@ -18,18 +18,18 @@ function about(io::IO, value::T) where {T}
datasize = sizeof(value)
netsize = Base.summarysize(value)
infosize = if typesize == datasize == netsize
styled"{about_bytes:$(join(humansize(typesize)))}."
S"{about_bytes:$(join(humansize(typesize)))}."
elseif typesize == datasize <= netsize
styled"{about_bytes:$(join(humansize(typesize)))} directly \
S"{about_bytes:$(join(humansize(typesize)))} directly \
(referencing {about_bytes:$(join(humansize(netsize)))} in total)"
elseif typesize == datasize > netsize
styled"{about_bytes:$(join(humansize(typesize)))} directly \
S"{about_bytes:$(join(humansize(typesize)))} directly \
({warning:!} referencing {about_bytes:$(join(humansize(netsize)))} in total, \
{warning:strangely less than the direct, \
{underline,link={https://github.com/tecosaur/About.jl}:\
please open an issue on About.jl with this example}})"
else # all different
styled"{about_bytes:$(join(humansize(typesize)))} directly \
S"{about_bytes:$(join(humansize(typesize)))} directly \
(referencing {about_bytes:$(join(humansize(netsize)))} in total, \
holding {about_bytes:$(join(humansize(datasize)))} of data)"
end
@ -61,7 +61,7 @@ function memorylayout(io::IO, value::T) where {T}
return
end
if Base.issingletontype(T)
println(io, styled"{italic:singelton}")
println(io, S"{italic:singelton}")
return
end
sinfo = structinfo(T)
@ -80,15 +80,15 @@ function memorylayout(io::IO, value::T) where {T}
aio = AnnotatedIOBuffer()
fvalue = getfield(value, name)
if Base.issingletontype(typeof(fvalue))
push!(freprs, styled"{shadow:singleton}")
push!(freprs, S"{shadow:singleton}")
elseif size == 0
push!(freprs, styled"{error:??}")
push!(freprs, S"{error:??}")
elseif ispointer
try
pt = pointer(fvalue)
push!(freprs, styled"{about_pointer:@ $(sprint(show, UInt64(pt)))}")
push!(freprs, S"{about_pointer:@ $(sprint(show, UInt64(pt)))}")
catch
push!(freprs, styled"{about_pointer:Ptr?}")
push!(freprs, S"{about_pointer:Ptr?}")
end
else
memorylayout(IOContext(aio, :compact => true), fvalue)
@ -107,9 +107,9 @@ function memorylayout(io::IO, value::T) where {T}
showwidth = width - reprwidth
for (face, name, type, size, brepr, shown) in zip(ffaces, fnames, ftypes, fsizes, freprs, fshows)
println(io, ' ',
styled"{$face:$(lpad(name, namewidth)){shadow:::}$(rpad(struncate(type, typewidth, \"\", :right), typewidth)) $(lpad(size, sizewidth))}",
' ', rpad(struncate(brepr, reprwidth, styled" {shadow:…} "), reprwidth),
' ', face!(struncate(shown, showwidth, styled" {shadow:…} "), face))
S"{$face:$(lpad(name, namewidth)){shadow:::}$(rpad(struncate(type, typewidth, \"\", :right), typewidth)) $(lpad(size, sizewidth))}",
' ', rpad(struncate(brepr, reprwidth, S" {shadow:…} "), reprwidth),
' ', face!(struncate(shown, showwidth, S" {shadow:…} "), face))
end
memorylayout(io, T)
end
@ -121,7 +121,7 @@ function memorylayout(io::IO, value::Bool)
if get(io, :compact, false) == true
print(io, bits)
else
println(io, "\n ", bits, styled" {bold:=} $value")
println(io, "\n ", bits, S" {bold:=} $value")
end
end
@ -134,7 +134,7 @@ function memorylayout(io::IO, value::Union{UInt8, UInt16, UInt32, UInt64, UInt12
print(io, bits)
else
println(io, "\n ", bits, ifelse(sizeof(value) > 4, "\n", ""),
styled" {bold:=} $value")
S" {bold:=} $value")
end
end
@ -152,7 +152,7 @@ function memorylayout(io::IO, value::Union{Int8, Int16, Int32, Int64, Int128})
else
signstr = ifelse(value < 0, '-', '+')
println(io, "\n ", bits, ifelse(sizeof(value) > 4, "\n", ""),
styled" {bold:=} {$(NUMBER_BIT_FACES.sign):$signstr}$(abs(value))")
S" {bold:=} {$(NUMBER_BIT_FACES.sign):$signstr}$(abs(value))")
end
end
@ -164,7 +164,7 @@ memorylayout(io::IO, float::Core.BFloat16) = floatlayout(io, float, 8)
function floatlayout(io::IO, float::AbstractFloat, expbits::Int)
fsign, fexp, fmant = NUMBER_BIT_FACES.sign, NUMBER_BIT_FACES.exponent, NUMBER_BIT_FACES.mantissa
bitstr = bitstring(float)
hl_bits = styled"{$fsign:$(bitstr[1])}{$fexp:$(bitstr[2:expbits+1])}{$fmant:$(bitstr[expbits+2:end])}"
hl_bits = S"{$fsign:$(bitstr[1])}{$fexp:$(bitstr[2:expbits+1])}{$fmant:$(bitstr[expbits+2:end])}"
if get(io, :compact, false) == true
print(io, hl_bits)
else
@ -188,12 +188,12 @@ function floatlayout(io::IO, float::AbstractFloat, expbits::Int)
eright = (expbits - 3) - eleft
fleft = (fracbits - 3) ÷ 2
fright = (fracbits - 3) - fleft
styled"{$fsign:╨}{$fexp:└$('─'^eleft)$('─'^eright)┘}{$fmant:└$('─'^fleft)$('─'^fright)┘}"
S"{$fsign:╨}{$fexp:└$('─'^eleft)$('─'^eright)┘}{$fmant:└$('─'^fleft)$('─'^fright)┘}"
end
hl_vals = styled"{$fsign,bold:$sign}{$fexp:$expstr}{bold:×}{$fmant:$fracstr}"
hl_more = styled" {$fexp:exponent}$(' '^17){$fmant:mantissa / fraction}"
hl_vals = S"{$fsign,bold:$sign}{$fexp:$expstr}{bold:×}{$fmant:$fracstr}"
hl_more = S" {$fexp:exponent}$(' '^17){$fmant:mantissa / fraction}"
println(io, "\n ", hl_bits, " \n ", hl_info, "\n ", hl_vals,
styled"\n {bold:=} ", if -8 < exponent < 8
S"\n {bold:=} ", if -8 < exponent < 8
Base.Ryu.writefixed(float, fracdp)
else Base.Ryu.writeexp(float, fracdp) end)
end
@ -222,7 +222,7 @@ function memorylayout(io::IO, char::Char)
length(ubytes) - 2)
else 1:0 end
chunk_coloring = [Pair{UnitRange{Int}, Symbol}[] for _ in 1:length(chunks)]
ustr = styled"{bold:U+$(lpad(join(ubytes), 4, '0'))}"
ustr = S"{bold:U+$(lpad(join(ubytes), 4, '0'))}"
for (i, b, color) in zip(1:length(ubytes),
collect(eachindex(ustr))[end-length(ubytes)+1:end],
Iterators.cycle(Iterators.reverse(FACE_CYCLE)))
@ -250,7 +250,7 @@ function memorylayout(io::IO, char::Char)
else
'┌' * cpad(ubyte, width-2, '─') * '┐'
end
print(io, styled"{$color:$byte_brace}")
print(io, S"{$color:$byte_brace}")
clean_jump && print(io, " ")
if does_byte_jump && !clean_jump
push!(chunk_coloring[1 + current_bit ÷ 8], (1 + current_bit % 8):8 => color)
@ -266,7 +266,7 @@ function memorylayout(io::IO, char::Char)
for (i, (chunk, coloring)) in enumerate(zip(chunks, chunk_coloring))
cbits = bitstring(chunk)
cstr = if i > nchunks
styled"{shadow:$cbits}"
S"{shadow:$cbits}"
else
leadingbits = if i == 1; byte0leading else 2 end
leading = cbits[1:leadingbits]
@ -274,7 +274,7 @@ function memorylayout(io::IO, char::Char)
for (; match) in eachmatch(r"1+", rest)
face!(match, :underline)
end
cstr = styled"{shadow:$leading}$rest"
cstr = S"{shadow:$leading}$rest"
for (range, color) in coloring
face!(cstr, range, color)
end
@ -286,10 +286,10 @@ function memorylayout(io::IO, char::Char)
println(io)
for chunk in chunks
byte = lpad(string(chunk, base=16), 2, '0')
print(io, styled" {shadow:└─0x$(byte)─┘}")
print(io, S" {shadow:└─0x$(byte)─┘}")
end
print(io, "\n = ", ustr)
Base.isoverlong(char) && print(io, styled" {error:[overlong]}")
Base.isoverlong(char) && print(io, S" {error:[overlong]}")
println(io)
end
end
@ -331,7 +331,7 @@ const CONTROL_CHARACTERS =
function elaboration(io::IO, char::Char)
c0index = findfirst(c -> first(c) == char, CONTROL_CHARACTERS)
stychr = styled"{julia_char:$(sprint(show, char))}"
stychr = S"{julia_char:$(sprint(show, char))}"
if !isnothing(c0index)
cshort, cname, cinfo = last(CONTROL_CHARACTERS[c0index])
println(io, "\n Control character ", stychr, ": ", cname, " ($cshort)",
@ -353,15 +353,15 @@ function elaboration(io::IO, char::Char)
println(io, "\n ASCII $kind ", stychr)
elseif char in ('Ç':'ø'..., 'Ø', 'á':'Ñ'..., 'Á':'À', 'ã', 'Ã', 'ð':'Ï'..., 'Ó':'Ý')
println(io, "\n Extended ASCII accented letter ", stychr,
styled" ({julia_number:0x$(string(UInt8(char), base=16))})")
S" ({julia_number:0x$(string(UInt8(char), base=16))})")
elseif Base.isoverlong(char)
elseif codepoint(char) in 128:255
println(io, "\n Extended ASCII symbol ", stychr,
styled" ({shadow:0x$(string(Int(char), base=16))})")
S" ({shadow:0x$(string(Int(char), base=16))})")
else
catstr = Base.Unicode.category_string(char)
catabr = Base.Unicode.category_abbrev(char)
println(io, styled"\n Unicode $stychr, category: $catstr ($catabr)")
println(io, S"\n Unicode $stychr, category: $catstr ($catabr)")
end
end