Setup.jl/src/termsetup.jl

164 lines
5.2 KiB
Julia

import Base.cd
"""
termtitle(dir)
Set the terminal title suffix to `dir`.
The home directory is replaced with "~", and trailing slash removed.
"""
function termtitle(dir)
if ENV["TERM"] != "dumb"
print("\e]0;Julia ● " *
replace(replace(dir, homedir() => "~"), r"/$" => "") *
"\a")
end
end
function replsetup()
atreplinit() do repl
if !isdefined(repl, :interface)
repl.interface = REPL.setup_interface(repl)
end
if Main.InteractiveUtils.editor().exec[1] == "e"
Main.InteractiveUtils.define_editor(
r"\be", wait=false) do cmd, path, line
`$cmd +$line $path`
end
Main.InteractiveUtils.define_editor(
r"\be\b.*\s(-w|--wait|-t|--tty)", wait=true) do cmd, path, line
`$cmd +$line $path`
end
end
if get(ENV, "INSIDE_EMACS", nothing) != "vterm"
print("\e[5 q")
end
@static if VERSION >= v"1.9"
# Reset title every prompt
repl.interface.modes[1].prompt = () ->
(termtitle(pwd()); REPL.contextual_prompt(repl, REPL.JULIA_PROMPT)())
end
end
@eval function Base.cd(dir::AbstractString)
err = ccall(:uv_chdir, Cint, (Cstring,), dir)
err < 0 && Base.uv_error("cd($(repr(dir)))", err)
termtitle(dir)
return nothing
end
end
clearterm() = print("\e[2J")
function termcode()
REPL.Terminals.raw!(REPL.TerminalMenus.terminal, true)
print("\eP+q544e\e\\")
output = @task readuntil(stdin, "\e\\")
schedule(output)
Timer(0.05) do _
istaskdone(output) || Base.throwto(output, InterruptException())
end
value = try
fetch(output)
catch
""
end
REPL.Terminals.raw!(REPL.TerminalMenus.terminal, false)
String(hex2bytes(last(split(value, '='))))
end
const TERM_COLORS = Ref(Dict{Symbol, @NamedTuple{r::UInt8, g::UInt8, b::UInt8}}())
function termcolors(; refresh::Bool=false)
!refresh && !isempty(TERM_COLORS[]) && return TERM_COLORS[]
REPL.Terminals.raw!(REPL.TerminalMenus.terminal, true)
output = @task _termcolors()
schedule(output)
Timer(0.5) do _
istaskdone(output) || Base.throwto(output, InterruptException())
end
colors = try
fetch(output)
catch end
REPL.Terminals.raw!(REPL.TerminalMenus.terminal, false)
if !isnothing(colors)
TERM_COLORS[] = colors
end
TERM_COLORS[]
end
function termtheme(; refresh::Bool=false)
colors = termcolors(; refresh)
white = get(colors, :white, (r=0x80, g=0x80, b=0x80))
black = get(colors, :black, (r=0x80, g=0x80, b=0x80))
fglevel, bglevel = sum(values(white)), sum(values(black))
if fglevel > bglevel
:dark
elseif fglevel < bglevel
:light
else
:unknown
end
end
function _termcolors()
RGBTuple = NamedTuple{(:r, :g, :b), NTuple{3, UInt8}}
colors = Dict{Symbol, RGBTuple}()
function readcolor(io::IO, n::Integer)
if read(io, Char) == '\e'
entry = readuntil(io, "\e\\")
m = match(r"^]4;(\d+);rgb:([0-9a-f]+)/([0-9a-f]+)/([0-9a-f]+)$", entry)
if !isnothing(m) && m.captures[1] == string(n)
r = parse(UInt8, m.captures[2][1:2], base=16)
g = parse(UInt8, m.captures[3][1:2], base=16)
b = parse(UInt8, m.captures[4][1:2], base=16)
RGBTuple((r, g, b))
end
end
end
colornames = [:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white,
:light_black, :light_red, :light_green, :light_yellow,
:light_blue, :light_magenta, :light_cyan, :light_white]
for (index, color) in enumerate(colornames)
print(stdout, "\e]4;$(index-1);?\e\\")
flush(stdout)
rgb = readcolor(stdin, index - 1)
if !isnothing(rgb)
colors[color] = rgb
end
end
colors
end
const TERM_IMAGE_PACKAGES = let pkg(uuid, name) = Base.PkgId(Base.UUID(uuid), name)
kitty = (pkg("b7fa5abe-5c7d-46c6-a1ae-1026d0d509b9", "KittyTerminalImages"),
:(KittyTerminalImages.pushKittyDisplay!()))
# TODO: sixel
Dict("xterm-kitty" => kitty)
end
const ATTEMPTED_TERMIMAGE_LOAD = Ref(false)
function load_termimage_pkg(autoinstall::Bool=true)
ATTEMPTED_TERMIMAGE_LOAD[] && return
pkg, pkgsetup = get(TERM_IMAGE_PACKAGES, Setup.TERM[], (nothing, nothing))
if !isnothing(pkg) && !haskey(Base.loaded_modules, pkg)
if !isnothing(Base.find_package(pkg.name))
ATTEMPTED_TERMIMAGE_LOAD[] = true
# Don't to this async because it may affect a `display` call
# that's just about to occur.
Core.eval(Main, :(import $(Symbol(pkg.name)); $pkgsetup))
elseif autoinstall
Pkg = lazypkg()
currentproj = Pkg.project().path
try
Pkg.activate()
Pkg.add(pkg.name)
load_termimage_pkg(false)
finally
Pkg.activate(currentproj)
end
else
@info "Consider installing $(pkg.name) for better in-terminal image previews"
end
end
end