Skip to content

Commit faacad2

Browse files
committed
Split default+modified faces into base/light/dark
1 parent 2c61618 commit faacad2

File tree

3 files changed

+81
-44
lines changed

3 files changed

+81
-44
lines changed

src/faces.jl

Lines changed: 72 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -403,20 +403,23 @@ const FACES = let default = Dict{Symbol, Face}(
403403
:bright_magenta => (r = 0xbf, g = 0x60, b = 0xca),
404404
:bright_cyan => (r = 0x26, g = 0xc6, b = 0xda),
405405
:bright_white => (r = 0xf6, g = 0xf5, b = 0xf4))
406-
(; default, basecolors,
406+
(themes = (base = default, light = Dict{Symbol, Face}(), dark = Dict{Symbol, Face}()),
407+
modifications = (base = Dict{Symbol, Face}(), light = Dict{Symbol, Face}(), dark = Dict{Symbol, Face}()),
407408
current = ScopedValue(copy(default)),
408-
modifications = ScopedValue(Dict{Symbol, Face}()),
409+
basecolors = basecolors,
409410
lock = ReentrantLock())
410411
end
411412

412413
## Adding and resetting faces ##
413414

414415
"""
415-
addface!(name::Symbol => default::Face)
416+
addface!(name::Symbol => default::Face, theme::Symbol = :base)
416417
417418
Create a new face by the name `name`. So long as no face already exists by this
418-
name, `default` is added to both `FACES``.default` and (a copy of) to
419-
`FACES`.`current`, with the current value returned.
419+
name, `default` is added to both `FACES.themes[theme]` and (a copy of) to
420+
`FACES.current`, with the current value returned.
421+
422+
The `theme` should be either `:base`, `:light`, or `:dark`.
420423
421424
Should the face `name` already exist, `nothing` is returned.
422425
@@ -429,11 +432,12 @@ Face (sample)
429432
underline: true
430433
```
431434
"""
432-
function addface!((name, default)::Pair{Symbol, Face})
433-
@lock FACES.lock if !haskey(FACES.default, name)
434-
FACES.default[name] = default
435-
FACES.current[][name] = if haskey(FACES.current[], name)
436-
merge(copy(default), FACES.current[][name])
435+
function addface!((name, default)::Pair{Symbol, Face}, theme::Symbol = :base)
436+
current = FACES.current[]
437+
@lock FACES.lock if !haskey(FACES.themes[theme], name)
438+
FACES.themes[theme][name] = default
439+
current[name] = if haskey(current, name)
440+
merge(copy(default), current[name])
437441
else
438442
copy(default)
439443
end
@@ -449,10 +453,12 @@ function resetfaces!()
449453
@lock FACES.lock begin
450454
current = FACES.current[]
451455
empty!(current)
452-
for (key, val) in FACES.default
456+
for (key, val) in FACES.themes.base
453457
current[key] = val
454458
end
455-
empty!(FACES.modifications[])
459+
if current === FACES.current.default # Only when top-level
460+
map(empty!, values(FACES.modifications))
461+
end
456462
current
457463
end
458464
end
@@ -466,13 +472,15 @@ If the face `name` does not exist, nothing is done and `nothing` returned.
466472
In the unlikely event that the face `name` does not have a default value,
467473
it is deleted, a warning message is printed, and `nothing` returned.
468474
"""
469-
function resetfaces!(name::Symbol)
470-
@lock FACES.lock if !haskey(FACES.current[], name)
471-
elseif haskey(FACES.default, name)
472-
delete!(FACES.modifications[], name)
473-
FACES.current[][name] = copy(FACES.default[name])
475+
function resetfaces!(name::Symbol, theme::Symbol = :base)
476+
current = FACES.current[]
477+
@lock FACES.lock if !haskey(current, name) # Nothing to reset
478+
elseif haskey(FACES.themes[theme], name)
479+
current === FACES.current.default &&
480+
delete!(FACES.modifications[theme], name)
481+
current[name] = copy(FACES.themes[theme][name])
474482
else # This shouldn't happen
475-
delete!(FACES.current[], name)
483+
delete!(current, name)
476484
@warn """The face $name was reset, but it had no default value, and so has been deleted instead!,
477485
This should not have happened, perhaps the face was added without using `addface!`?"""
478486
end
@@ -658,18 +666,17 @@ Face (sample)
658666
foreground: #ff0000
659667
```
660668
"""
661-
function loadface!((name, update)::Pair{Symbol, Face})
669+
function loadface!((name, update)::Pair{Symbol, Face}, theme::Symbol = :base)
662670
@lock FACES.lock begin
663-
mface = get(FACES.modifications[], name, nothing)
664-
if !isnothing(mface)
665-
update = merge(mface, update)
666-
end
667-
FACES.modifications[][name] = update
668-
cface = get(FACES.current[], name, nothing)
669-
if !isnothing(cface)
670-
update = merge(cface, update)
671+
current = FACES.current[]
672+
if FACES.current.default === current # Only save top-level modifications
673+
mface = get(FACES.modifications[theme], name, nothing)
674+
isnothing(mface) || (update = merge(mface, update))
675+
FACES.modifications[theme][name] = update
671676
end
672-
FACES.current[][name] = update
677+
cface = get(current, name, nothing)
678+
isnothing(cface) || (update = merge(cface, update))
679+
current[name] = update
673680
end
674681
end
675682

@@ -684,7 +691,9 @@ end
684691
685692
For each face specified in `Dict`, load it to `FACES``.current`.
686693
"""
687-
function loaduserfaces!(faces::Dict{String, Any}, prefix::Union{String, Nothing}=nothing)
694+
function loaduserfaces!(faces::Dict{String, Any}, prefix::Union{String, Nothing}=nothing, theme::Symbol = :base)
695+
theme == :base && prefix map(String, setdiff(keys(FACES.themes), (:base,))) &&
696+
return loaduserfaces!(faces, nothing, Symbol(prefix))
688697
for (name, spec) in faces
689698
fullname = if isnothing(prefix)
690699
name
@@ -694,7 +703,7 @@ function loaduserfaces!(faces::Dict{String, Any}, prefix::Union{String, Nothing}
694703
fspec = filter((_, v)::Pair -> !(v isa Dict), spec)
695704
fnest = filter((_, v)::Pair -> v isa Dict, spec)
696705
!isempty(fspec) &&
697-
loadface!(Symbol(fullname) => convert(Face, fspec))
706+
loadface!(Symbol(fullname) => convert(Face, fspec), theme)
698707
!isempty(fnest) &&
699708
loaduserfaces!(fnest, fullname)
700709
end
@@ -784,22 +793,50 @@ function recolor(f::Function)
784793
end
785794

786795
function setcolors!(color::Vector{Pair{Symbol, RGBTuple}})
787-
@lock recolor_lock begin
796+
lock(recolor_lock)
797+
lock(FACES.lock)
798+
try
799+
# Apply colors
800+
fg, bg = nothing, nothing
788801
for (name, rgb) in color
789802
FACES.basecolors[name] = rgb
803+
if name === :foreground
804+
fg = rgb
805+
elseif name === :background
806+
bg = rgb
807+
end
790808
end
809+
newtheme = if !isnothing(fg) && !isnothing(bg)
810+
:unknown
811+
else
812+
ifelse(sum(fg) > sum(bg), :light, :dark)
813+
end
814+
# Reset all themes to defaults
791815
current = FACES.current[]
792-
for (name, _) in FACES.modifications[]
793-
default = get(FACES.default, name, nothing)
816+
for theme in keys(FACES.themes), (name, _) in FACES.modifications[theme]
817+
default = get(FACES.themes.base, name, nothing)
794818
isnothing(default) && continue
795819
current[name] = default
796820
end
821+
if newtheme keys(FACES.themes)
822+
for (name, face) in FACES.themes[newtheme]
823+
current[name] = merge(current[name], face)
824+
end
825+
end
826+
# Run recolor hooks
797827
for hook in recolor_hooks
798828
hook()
799829
end
800-
for (name, face) in FACES.modifications[]
801-
current[name] = merge(current[name], face)
830+
# Layer on modifications
831+
for theme in keys(FACES.themes)
832+
theme (:base, newtheme) || continue
833+
for (name, face) in FACES.modifications[theme]
834+
current[name] = merge(current[name], face)
835+
end
802836
end
837+
finally
838+
unlock(FACES.lock)
839+
unlock(recolor_lock)
803840
end
804841
end
805842

src/io.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ function _ansi_writer(string_writer::F, io::IO, s::Union{<:AnnotatedString, SubS
243243
# We need to make sure that the customisations are loaded
244244
# before we start outputting any styled content.
245245
load_customisations!()
246-
default = FACES.default[:default]
246+
default = FACES.themes.base[:default]
247247
if get(io, :color, false)::Bool
248248
buf = IOBuffer() # Avoid the overhead in repeatedly printing to `stdout`
249249
lastface::Face = default

test/runtests.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -254,35 +254,35 @@ end
254254
strikethrough: false
255255
inverse: false\
256256
"""
257-
@test sprint(show, MIME("text/plain"), FACES.default[:red], context = :color => true) |> choppkg ==
257+
@test sprint(show, MIME("text/plain"), FACES.themes.base[:red], context = :color => true) |> choppkg ==
258258
"""
259259
Face (\e[31msample\e[39m)
260260
foreground: \e[31m■\e[39m red\
261261
"""
262-
@test sprint(show, FACES.default[:red]) |> choppkg ==
262+
@test sprint(show, FACES.themes.base[:red]) |> choppkg ==
263263
"Face(foreground=SimpleColor(:red))"
264-
@test sprint(show, MIME("text/plain"), FACES.default[:red], context = :compact => true) |> choppkg ==
264+
@test sprint(show, MIME("text/plain"), FACES.themes.base[:red], context = :compact => true) |> choppkg ==
265265
"Face(foreground=SimpleColor(:red))"
266-
@test sprint(show, MIME("text/plain"), FACES.default[:red], context = (:compact => true, :color => true)) |> choppkg ==
266+
@test sprint(show, MIME("text/plain"), FACES.themes.base[:red], context = (:compact => true, :color => true)) |> choppkg ==
267267
"Face(\e[31msample\e[39m)"
268-
@test sprint(show, MIME("text/plain"), FACES.default[:highlight], context = :compact => true) |> choppkg ==
268+
@test sprint(show, MIME("text/plain"), FACES.themes.base[:highlight], context = :compact => true) |> choppkg ==
269269
"Face(inverse=true, inherit=[:emphasis])"
270270
with_terminfo(vt100) do # Not truecolor capable
271-
@test sprint(show, MIME("text/plain"), FACES.default[:region], context = :color => true) |> choppkg ==
271+
@test sprint(show, MIME("text/plain"), FACES.themes.base[:region], context = :color => true) |> choppkg ==
272272
"""
273273
Face (\e[48;5;237msample\e[49m)
274274
background: \e[38;5;237m■\e[39m #3a3a3a\
275275
"""
276276
end
277277
with_terminfo(fancy_term) do # Truecolor capable
278-
@test sprint(show, MIME("text/plain"), FACES.default[:region], context = :color => true) |> choppkg ==
278+
@test sprint(show, MIME("text/plain"), FACES.themes.base[:region], context = :color => true) |> choppkg ==
279279
"""
280280
Face (\e[48;2;58;58;58msample\e[49m)
281281
background: \e[38;2;58;58;58m■\e[39m #3a3a3a\
282282
"""
283283
end
284284
with_terminfo(vt100) do # Ensure `enter_reverse_mode` exists
285-
@test sprint(show, MIME("text/plain"), FACES.default[:highlight], context = :color => true) |> choppkg ==
285+
@test sprint(show, MIME("text/plain"), FACES.themes.base[:highlight], context = :color => true) |> choppkg ==
286286
"""
287287
Face (\e[34m\e[7msample\e[39m\e[27m)
288288
inverse: true

0 commit comments

Comments
 (0)