Skip to content

Commit bb85b9d

Browse files
KristofferCtecosaur
authored andcommitted
Backport main changes: 4d04102 to f6035eb
1 parent 51e3290 commit bb85b9d

File tree

6 files changed

+84
-45
lines changed

6 files changed

+84
-45
lines changed

docs/src/internals.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ StyledStrings.resetfaces!
2525
StyledStrings.termcolor
2626
StyledStrings.termcolor24bit
2727
StyledStrings.termcolor8bit
28+
StyledStrings.load_customisations!
2829
```
2930

3031
## Styled Markup parsing

src/StyledStrings.jl

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,35 @@ include("legacy.jl")
2323

2424
using .StyledMarkup
2525

26-
function __init__()
27-
term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb")
28-
global current_terminfo = load_terminfo(term_env)
26+
const HAVE_LOADED_CUSTOMISATIONS = Base.Threads.Atomic{Bool}(false)
27+
28+
"""
29+
load_customisations!(; force::Bool=false)
30+
31+
Load customisations from the user's `faces.toml` file, if it exists as well as
32+
the current environment.
33+
34+
This function should be called before producing any output in situations where
35+
the user's customisations should be considered.
36+
37+
Unless `force` is set, customisations are only applied when this function is
38+
called for the first time, and subsequent calls are a no-op.
39+
"""
40+
function load_customisations!(; force::Bool=false)
41+
!force && HAVE_LOADED_CUSTOMISATIONS[] && return
2942
if !isempty(DEPOT_PATH)
3043
userfaces = joinpath(first(DEPOT_PATH), "config", "faces.toml")
3144
isfile(userfaces) && loaduserfaces!(userfaces)
3245
end
3346
Legacy.load_env_colors!()
47+
HAVE_LOADED_CUSTOMISATIONS[] = true
48+
nothing
49+
end
50+
51+
function __init__()
52+
term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb")
53+
global current_terminfo = load_terminfo(term_env)
54+
load_customisations!()
3455
end
3556

3657
if generating_output()

src/faces.jl

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,15 @@ struct SimpleColor
2727
end
2828

2929
SimpleColor(r::Integer, g::Integer, b::Integer) = SimpleColor((; r=UInt8(r), g=UInt8(g), b=UInt8(b)))
30-
SimpleColor(rgb::UInt32) = SimpleColor(reverse(reinterpret(UInt8, [rgb]))[2:end]...)
30+
31+
function SimpleColor(rgb::UInt32)
32+
b, g, r, _ = @static if VERSION >= v"1.10"
33+
reinterpret(UInt8, [htol(rgb)])
34+
else
35+
reinterpret(UInt8, [htol(rgb)])
36+
end
37+
SimpleColor(r, g, b)
38+
end
3139

3240
Base.convert(::Type{SimpleColor}, rgb::RGBTuple) = SimpleColor(rgb)
3341
Base.convert(::Type{SimpleColor}, namedcolor::Symbol) = SimpleColor(namedcolor)
@@ -477,7 +485,7 @@ function withfaces(f, keyvals_itr)
477485
FACES.current[][name] = face
478486
elseif face isa Symbol
479487
FACES.current[][name] =
480-
something(get(old, face, nothing), get(FACES.current[], face, Face()))
488+
something(get(old, face, nothing), get(Face, FACES.current[], face))
481489
elseif face isa Vector{Symbol}
482490
FACES.current[][name] = Face(inherit=face)
483491
elseif haskey(FACES.current[], name)
@@ -511,25 +519,30 @@ later faces taking priority.
511519
"""
512520
function Base.merge(a::Face, b::Face)
513521
if isempty(b.inherit)
514-
Face(ifelse(isnothing(b.font), a.font, b.font),
515-
if isnothing(b.height) a.height
516-
elseif isnothing(a.height) b.height
517-
elseif b.height isa Int b.height
518-
elseif a.height isa Int round(Int, a.height * b.height)
519-
else a.height * b.height end,
520-
ifelse(isnothing(b.weight), a.weight, b.weight),
521-
ifelse(isnothing(b.slant), a.slant, b.slant),
522-
ifelse(isnothing(b.foreground), a.foreground, b.foreground),
523-
ifelse(isnothing(b.background), a.background, b.background),
524-
ifelse(isnothing(b.underline), a.underline, b.underline),
525-
ifelse(isnothing(b.strikethrough), a.strikethrough, b.strikethrough),
526-
ifelse(isnothing(b.inverse), a.inverse, b.inverse),
522+
# Extract the heights to help type inference a bit to be able
523+
# to narrow the types in e.g. `aheight * bheight`
524+
aheight = a.height
525+
bheight = b.height
526+
abheight = if isnothing(bheight) aheight
527+
elseif isnothing(aheight) bheight
528+
elseif bheight isa Int bheight
529+
elseif aheight isa Int round(Int, aheight * bheight)
530+
else aheight * bheight end
531+
Face(if isnothing(b.font) a.font else b.font end,
532+
abheight,
533+
if isnothing(b.weight) a.weight else b.weight end,
534+
if isnothing(b.slant) a.slant else b.slant end,
535+
if isnothing(b.foreground) a.foreground else b.foreground end,
536+
if isnothing(b.background) a.background else b.background end,
537+
if isnothing(b.underline) a.underline else b.underline end,
538+
if isnothing(b.strikethrough) a.strikethrough else b.strikethrough end,
539+
if isnothing(b.inverse) a.inverse else b.inverse end,
527540
a.inherit)
528541
else
529542
b_noinherit = Face(
530543
b.font, b.height, b.weight, b.slant, b.foreground, b.background,
531544
b.underline, b.strikethrough, b.inverse, Symbol[])
532-
b_inheritance = map(fname -> get(FACES.current[], fname, Face()), Iterators.reverse(b.inherit))
545+
b_inheritance = map(fname -> get(Face, FACES.current[], fname), Iterators.reverse(b.inherit))
533546
b_resolved = merge(foldl(merge, b_inheritance), b_noinherit)
534547
merge(a, b_resolved)
535548
end
@@ -539,6 +552,11 @@ Base.merge(a::Face, b::Face, others::Face...) = merge(merge(a, b), others...)
539552

540553
## Getting the combined face from a set of properties ##
541554

555+
# Putting these inside `getface` causes the julia compiler to box it
556+
_mergedface(face::Face) = face
557+
_mergedface(face::Symbol) = get(Face, FACES.current[], face)
558+
_mergedface(faces::Vector) = mapfoldl(_mergedface, merge, Iterators.reverse(faces))
559+
542560
"""
543561
getface(faces)
544562
@@ -547,10 +565,7 @@ Obtain the final merged face from `faces`, an iterator of
547565
"""
548566
function getface(faces)
549567
isempty(faces) && return FACES.current[][:default]
550-
mergedface(face::Face) = face
551-
mergedface(face::Symbol) = get(FACES.current[], face, Face())
552-
mergedface(faces::Vector) = mapfoldl(mergedface, merge, Iterators.reverse(faces))
553-
combined = mapfoldl(mergedface, merge, faces)::Face
568+
combined = mapfoldl(_mergedface, merge, faces)::Face
554569
if !isempty(combined.inherit)
555570
combined = merge(Face(), combined)
556571
end
@@ -568,7 +583,7 @@ function getface(annotations::Vector{Pair{Symbol, Any}})
568583
end
569584

570585
getface(face::Face) = merge(FACES.current[][:default], merge(Face(), face))
571-
getface(face::Symbol) = getface(get(FACES.current[], face, Face()))
586+
getface(face::Symbol) = getface(get(Face, FACES.current[], face))
572587

573588
"""
574589
getface()

src/io.jl

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,18 @@ const ANSI_4BIT_COLORS = Dict{Symbol, Int}(
3232
"""
3333
ansi_4bit_color_code(color::Symbol, background::Bool=false)
3434
35-
Provide the color code (30-37, 40-47, 90-97, 100-107) for `color`, as a string.
35+
Provide the color code (30-37, 40-47, 90-97, 100-107) for `color`, as an integer.
3636
When `background` is set the background variant will be provided, otherwise
3737
the provided code is for setting the foreground color.
3838
"""
3939
function ansi_4bit_color_code(color::Symbol, background::Bool=false)
40-
if haskey(ANSI_4BIT_COLORS, color)
41-
code = ANSI_4BIT_COLORS[color]
40+
code = get(ANSI_4BIT_COLORS, color, nothing)
41+
if code !== nothing
4242
code >= 8 && (code += 52)
4343
background && (code += 10)
44-
string(code + 30)
44+
code + 30
4545
else
46-
ifelse(background, "49", "39")
46+
ifelse(background, 49, 39)
4747
end
4848
end
4949

@@ -124,15 +124,17 @@ function termcolor(io::IO, color::SimpleColor, category::Char)
124124
elseif (fg = get(FACES.current[], color.value, getface()).foreground) != SimpleColor(color.value)
125125
termcolor(io, fg, category)
126126
else
127-
print(io, "\e[",
128-
if category == '3' || category == '4'
129-
ansi_4bit_color_code(color.value, category == '4')
130-
elseif category == '5'
131-
if haskey(ANSI_4BIT_COLORS, color.value)
132-
string("58;5;", ANSI_4BIT_COLORS[color.value])
133-
else "59" end
134-
end,
135-
'm')
127+
print(io, "\e[")
128+
if category == '3' || category == '4'
129+
print(io, ansi_4bit_color_code(color.value, category == '4'))
130+
elseif category == '5'
131+
if haskey(ANSI_4BIT_COLORS, color.value)
132+
print(io, "58;5;", ANSI_4BIT_COLORS[color.value])
133+
else
134+
print(io, "59")
135+
end
136+
end
137+
print(io, "m")
136138
end
137139
end
138140

src/regioniterator.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ function eachregion(s::SubString{<:AnnotatedString}, pos::UnitRange{Int}=firstin
7878
end
7979

8080
"""
81-
annotation_events(string::AbstractString, annots::Vector{Tuple{UnitRange{Int64}, Pair{Symbol, Any}}}, subregion::UnitRange{Int})
81+
annotation_events(string::AbstractString, annots::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, subregion::UnitRange{Int})
8282
annotation_events(string::AnnotatedString, subregion::UnitRange{Int})
8383
8484
Find all annotation "change events" that occur within a `subregion` of `annots`,
@@ -89,7 +89,7 @@ index::Int}` where `pos` is the position of the event, `active` is a boolean
8989
indicating whether the annotation is being activated or deactivated, and `index`
9090
is the index of the annotation in question.
9191
"""
92-
function annotation_events(s::AbstractString, annots::Vector{Tuple{UnitRange{Int64}, Pair{Symbol, Any}}}, subregion::UnitRange{Int})
92+
function annotation_events(s::AbstractString, annots::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, subregion::UnitRange{Int})
9393
events = Vector{NamedTuple{(:pos, :active, :index), Tuple{Int, Bool, Int}}}() # Position, Active?, Annotation index
9494
for (i, (region, _)) in enumerate(annots)
9595
if !isempty(intersect(subregion, region))

test/runtests.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -516,11 +516,11 @@ end
516516

517517
@testset "ANSI encoding" begin
518518
# 4-bit color
519-
@test StyledStrings.ansi_4bit_color_code(:cyan, false) == "36"
520-
@test StyledStrings.ansi_4bit_color_code(:cyan, true) == "46"
521-
@test StyledStrings.ansi_4bit_color_code(:bright_cyan, false) == "96"
522-
@test StyledStrings.ansi_4bit_color_code(:bright_cyan, true) == "106"
523-
@test StyledStrings.ansi_4bit_color_code(:nonexistant) == "39"
519+
@test StyledStrings.ansi_4bit_color_code(:cyan, false) == 36
520+
@test StyledStrings.ansi_4bit_color_code(:cyan, true) == 46
521+
@test StyledStrings.ansi_4bit_color_code(:bright_cyan, false) == 96
522+
@test StyledStrings.ansi_4bit_color_code(:bright_cyan, true) == 106
523+
@test StyledStrings.ansi_4bit_color_code(:nonexistant) == 39
524524
# 8-bit color
525525
@test sprint(StyledStrings.termcolor8bit, (r=0x40, g=0x63, b=0xd8), '3') == "\e[38;5;26m"
526526
@test sprint(StyledStrings.termcolor8bit, (r=0x38, g=0x98, b=0x26), '3') == "\e[38;5;28m"

0 commit comments

Comments
 (0)