More thoroughly theme the terminal
This commit is contained in:
parent
12bcf3a060
commit
7974004490
|
@ -5,36 +5,13 @@ using StyledStrings: Face, getface, resetfaces!, loadface!,
|
|||
|
||||
const THEMES = Dict{Symbol, Vector{Pair{Symbol, Face}}}()
|
||||
|
||||
function load(theme::Symbol, term::Bool=true)
|
||||
faces = get(THEMES, theme, nothing)
|
||||
isnothing(faces) && throw(ArgumentError("The doom theme $theme is not defined."))
|
||||
resetfaces!()
|
||||
foreach(loadface!, faces)
|
||||
term || return
|
||||
setcolor(id::String, ::Nothing) = print(stdout, "\e]", id, ";\a")
|
||||
setcolor(id::String, color::SimpleColor) = if color.value isa RGBTuple
|
||||
print(stdout, "\e]", id, ";rgb:", join(string.(values(color.value), base=16), '/'), "\a")
|
||||
else
|
||||
print(stdout, "\e]", id, ";", string(color.value), "\a")
|
||||
end
|
||||
(; foreground, background) = getface()
|
||||
setcolor("10", if foreground.value isa RGBTuple foreground end)
|
||||
setcolor("11", if background.value isa RGBTuple background end)
|
||||
cursor = getface(:cursor)
|
||||
setcolor("12", if cursor.background.value isa RGBTuple && cursor.background != background
|
||||
cursor.background end)
|
||||
end
|
||||
|
||||
function reset(term::Bool=true)
|
||||
resetfaces!()
|
||||
term || return
|
||||
print(stdout, "\e]10;\a\e]11;\a\e]12;\a")
|
||||
@eval for theme in readdir(joinpath(@__DIR__, "themes"))
|
||||
include("themes/$theme")
|
||||
end
|
||||
|
||||
function list()
|
||||
for theme in sort(keys(THEMES) |> collect)
|
||||
withfaces(Dict(THEMES[theme])) do
|
||||
print("\e[0m")
|
||||
withfaces(THEMES[theme]) do
|
||||
println(styled" $(rpad(theme, 30)) \
|
||||
{emphasis:■} {julia_funcall:■} {julia_symbol:■} {julia_type:■} {julia_string:■} \
|
||||
{(fg=red):■} {(fg=green):■} {(fg=yellow):■} {(fg=blue):■} \
|
||||
|
@ -43,8 +20,20 @@ function list()
|
|||
end
|
||||
end
|
||||
|
||||
@eval for theme in readdir(joinpath(@__DIR__, "themes"))
|
||||
include("themes/$theme")
|
||||
function load!(theme::Symbol, term::Bool=true)
|
||||
faces = get(THEMES, theme, nothing)
|
||||
isnothing(faces) && throw(ArgumentError("The doom theme $theme is not defined."))
|
||||
resetfaces!()
|
||||
foreach(loadface!, faces)
|
||||
term && theme_terminal!(faces)
|
||||
end
|
||||
|
||||
include("termtheme.jl")
|
||||
|
||||
function __init__()
|
||||
if isinteractive()
|
||||
atexit(() -> reset!(true))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
const ANSI_COLOR_CODES = (
|
||||
black = "0",
|
||||
red = "1",
|
||||
green = "2",
|
||||
yellow = "3",
|
||||
blue = "4",
|
||||
magenta = "5",
|
||||
cyan = "6",
|
||||
white = "7",
|
||||
bright_black = "8",
|
||||
grey = "8",
|
||||
gray = "8",
|
||||
bright_red = "9",
|
||||
bright_green = "10",
|
||||
bright_yellow = "11",
|
||||
bright_blue = "12",
|
||||
bright_magenta = "13",
|
||||
bright_cyan = "14",
|
||||
bright_white = "15",
|
||||
)
|
||||
|
||||
const OSC_PARAMS = (
|
||||
foreground = "10",
|
||||
background = "11",
|
||||
cursor = "12",
|
||||
highlight = "17",
|
||||
)
|
||||
|
||||
const INITIAL_TERM_COLOURING = Dict{Symbol, SimpleColor}();
|
||||
|
||||
function term_raw!(raw::Bool)
|
||||
Base.check_open(stdin)
|
||||
if Sys.iswindows() && Base.ispty(stdin)
|
||||
run((raw ? `stty raw -echo onlcr -ocrnl opost` : `stty sane`),
|
||||
stdin, stdout, stderr)
|
||||
true
|
||||
else
|
||||
ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid},Int32), stdin.handle::Ptr{Cvoid}, raw) != -1
|
||||
end
|
||||
end
|
||||
|
||||
function read_osc_response(timeout::Real = 0.05)
|
||||
outbytes = UInt8[]
|
||||
start = time()
|
||||
lock(stdin.cond)
|
||||
Base.iolock_begin()
|
||||
while start - time() < timeout && (isempty(outbytes) || last(outbytes) ∉ (UInt8('\\'), UInt8('\a'), UInt8('\3')))
|
||||
if bytesavailable(stdin.buffer) > 0
|
||||
push!(outbytes, read(stdin.buffer, UInt8))
|
||||
else
|
||||
stdin.readerror === nothing || throw(stdin.readerror)
|
||||
isopen(stdin) || break
|
||||
Base.start_reading(stdin) # ensure we are reading
|
||||
Base.iolock_end()
|
||||
# @info "Waiting for stdin.cond"
|
||||
wait(stdin.cond)
|
||||
# @info "Finished for stdin.cond"
|
||||
unlock(stdin.cond)
|
||||
Base.iolock_begin()
|
||||
lock(stdin.cond)
|
||||
end
|
||||
end
|
||||
Base.iolock_end()
|
||||
unlock(stdin.cond)
|
||||
String(outbytes)
|
||||
end
|
||||
|
||||
function interpret_osc_color(output::String)
|
||||
startswith(output, "\e]") || return
|
||||
seppos = findlast(';', output)
|
||||
isnothing(seppos) && return
|
||||
code = output[ncodeunits("\e]")+1:seppos-1]
|
||||
output = @view output[seppos+1:end]
|
||||
if endswith(output, "\e\\")
|
||||
output = @view output[begin:end-2]
|
||||
else endswith(output, "\a")
|
||||
output = @view output[begin:end-1]
|
||||
end
|
||||
if startswith(output, "rgb:")
|
||||
components = split(output[ncodeunits("rgb:")+1:end], '/')
|
||||
length(components) == 3 || return
|
||||
elseif startswith(output, "rgba:")
|
||||
components = split(output[ncodeunits("rgba:")+1:end], '/')
|
||||
length(components) == 4 || return
|
||||
else
|
||||
return
|
||||
end
|
||||
rgbstr = (components[1], components[2], components[3])
|
||||
validcolorhex(chex) =
|
||||
!isempty(chex) && all(c -> c in '0':'9' || c in 'a':'f' || c in 'A':'F', chex)
|
||||
all(validcolorhex, rgbstr) || return
|
||||
rgb = map(chex -> UInt8(parse(Int, chex, base=16) ÷ 16^(length(chex) - 2)), rgbstr)
|
||||
code, SimpleColor(rgb[1], rgb[2], rgb[3])
|
||||
end
|
||||
|
||||
function read_colours!(out::Dict{Symbol, SimpleColor})
|
||||
term_raw!(true)
|
||||
for code in values(OSC_PARAMS)
|
||||
print(stdout, "\e]", code, ";?\a")
|
||||
end
|
||||
for name in keys(OSC_PARAMS)
|
||||
val = read_osc_response() |> interpret_osc_color
|
||||
isnothing(val) && continue
|
||||
out[name] = last(val)
|
||||
end
|
||||
print(stdout, "\e]4;")
|
||||
join(stdout, values(ANSI_COLOR_CODES), ";?;")
|
||||
print(stdout, ";?\a")
|
||||
for _ in 1:length(ANSI_COLOR_CODES)
|
||||
val = read_osc_response() |> interpret_osc_color
|
||||
isnothing(val) && continue
|
||||
code, colour = val
|
||||
code = chopprefix(code, "4;")
|
||||
name = :_
|
||||
for (key, kcode) in pairs(ANSI_COLOR_CODES)
|
||||
if code == kcode
|
||||
name = key
|
||||
break
|
||||
end
|
||||
end
|
||||
out[name] = colour
|
||||
end
|
||||
term_raw!(false)
|
||||
out
|
||||
end
|
||||
|
||||
function set_termcolor!(id::Symbol, color::Union{SimpleColor, Nothing}=nothing)
|
||||
isempty(INITIAL_TERM_COLOURING) && read_colours!(INITIAL_TERM_COLOURING)
|
||||
default = get(INITIAL_TERM_COLOURING, id, nothing)
|
||||
isnothing(default) && return false # Only make changes than can be undone
|
||||
color = something(color, default)
|
||||
# Ignore non-RGBTuple colours because they'll be ugly
|
||||
color.value isa RGBTuple || return false
|
||||
code = if haskey(OSC_PARAMS, id)
|
||||
print(stdout, "\e]", OSC_PARAMS[id], ";rgb:")
|
||||
elseif haskey(ANSI_COLOR_CODES, id)
|
||||
print(stdout, "\e]4;" * ANSI_COLOR_CODES[id], ";rgb:")
|
||||
else
|
||||
return
|
||||
end
|
||||
join(stdout, string.(values(color.value), base=16), '/')
|
||||
print(stdout, "\a")
|
||||
true
|
||||
end
|
||||
|
||||
function theme_terminal!(theme::Vector{Pair{Symbol, Face}})
|
||||
get(Base.current_terminfo, :can_change, false) || return
|
||||
(; foreground, background) = getface()
|
||||
if set_termcolor!(:foreground, foreground)
|
||||
loadface!(:default => Face(foreground = :default))
|
||||
end
|
||||
if set_termcolor!(:background, background)
|
||||
loadface!(:default => Face(background = :default))
|
||||
end
|
||||
cursor = getface(:cursor)
|
||||
set_termcolor!(:cursor, if cursor.background != background; cursor.background end)
|
||||
highlight = getface(:highlight)
|
||||
set_termcolor!(:highlight, if highlight.background != background; highlight.background end)
|
||||
for name in keys(ANSI_COLOR_CODES)
|
||||
if set_termcolor!(name, getface(name).foreground)
|
||||
loadface!(name => Face(foreground = name))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function reset!(term::Bool=true)
|
||||
resetfaces!()
|
||||
term && get(Base.current_terminfo, :can_change, false) || return
|
||||
for key in keys(INITIAL_TERM_COLOURING)
|
||||
set_termcolor!(key)
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue