Initial commit
This commit is contained in:
commit
09f905f4ab
|
@ -0,0 +1 @@
|
|||
Manifest.toml
|
|
@ -0,0 +1,42 @@
|
|||
name = "Setup"
|
||||
uuid = "1e701ad1-9860-423b-8651-7f81d307e3cf"
|
||||
authors = ["TEC <git@tecosaur.net>"]
|
||||
version = "0.1.0"
|
||||
|
||||
[deps]
|
||||
BaseDirs = "18cc8868-cbac-4acf-b575-c8ff214dc66f"
|
||||
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
|
||||
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
|
||||
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
|
||||
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
|
||||
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
|
||||
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
|
||||
|
||||
[weakdeps]
|
||||
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
|
||||
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
|
||||
Gadfly = "c91e804a-d5a3-530f-b6f0-dfbca275c004"
|
||||
HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
|
||||
JpegTurbo = "b835a17e-a41a-41e7-81f0-2f016b05efe0"
|
||||
KittyTerminalImages = "b7fa5abe-5c7d-46c6-a1ae-1026d0d509b9"
|
||||
Netpbm = "f09324ee-3d7c-5217-9330-fc30815ba969"
|
||||
OhMyREPL = "5fb14364-9ced-5910-84b2-373655c76a03"
|
||||
PNGFiles = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883"
|
||||
QOI = "4b34888f-f399-49d4-9bb3-47ed5cae4e65"
|
||||
TiffImages = "731e570b-9d59-4bfa-96dc-6df516fadf69"
|
||||
|
||||
[extensions]
|
||||
cairomakie = "CairoMakie"
|
||||
gadfly = "Gadfly"
|
||||
hdf5 = "HDF5"
|
||||
image_jpeg = "JpegTurbo"
|
||||
image_netpbm = "Netpbm"
|
||||
image_png = "PNGFiles"
|
||||
image_qoi = "QOI"
|
||||
image_tiff = "TiffImages"
|
||||
ohmyrepl = "OhMyREPL"
|
||||
prettycolors = "Colors"
|
||||
|
||||
[compat]
|
||||
julia = "1.7"
|
|
@ -0,0 +1,85 @@
|
|||
#+title: Setup.jl
|
||||
#+author: TEC
|
||||
|
||||
This is my personal Julia configuration. It has grown far beyond my wildest imagination.
|
||||
|
||||
* Installation
|
||||
|
||||
#+begin_src shell
|
||||
julia --startup-file=no -e 'using Pkg; Pkg.add(url="https://git.tecosaur.net/tec/Setup.jl.git"); using Setup; Setup.install()'
|
||||
#+end_src
|
||||
|
||||
* Features
|
||||
** Autoloading
|
||||
|
||||
Instead of loading packages whose functionality might be needed at some point,
|
||||
load them on-demand. Lazy loading for the win!
|
||||
|
||||
The following packages are currently supported:
|
||||
+ The standard library (=Base64=, =CRC32c=, =Dates=, =Distributions=, =Downloads=,
|
||||
=LinearAlgebra=, =Markdown=, =Mmap=, =Pkg=, =Printf=, =REPL=, =Random=, =SHA=, =Serialisation=,
|
||||
=Statistics=, =StatsBase=, =TOML=, =Tar=, =Test=, =Threads=, =UUIDs=)
|
||||
+ =BaseDirs=
|
||||
+ =BenchmarkTools=
|
||||
+ =CSV=
|
||||
+ =Cthulhu=
|
||||
+ =DataFrames=
|
||||
+ =DataToolkit=
|
||||
+ =Debugger=
|
||||
+ =Distributions=
|
||||
+ =JET=
|
||||
+ =JSON3=
|
||||
+ =Org=
|
||||
+ =WhyNotEqual=
|
||||
|
||||
** Function/Structure inspection
|
||||
|
||||
Get a sense of a function or structure with the =about= function/macro.
|
||||
|
||||
** Environment tweaks
|
||||
*** Shorter activation
|
||||
|
||||
Add =a= as a shorthand for =activate=
|
||||
|
||||
*** Easier temp environments
|
||||
|
||||
Add =-t= as a shorthand for =--temp=, with the activate change this means opening a
|
||||
temporary environment is now a bit quicker:
|
||||
|
||||
#+begin_example
|
||||
pkg> a -t
|
||||
#+end_example
|
||||
|
||||
instead of
|
||||
|
||||
#+begin_example
|
||||
pkg> activate --temp
|
||||
#+end_example
|
||||
|
||||
|
||||
*** Environment stack
|
||||
|
||||
Load and unload named environments with the new =pkg> (un)stack= set of commands.
|
||||
|
||||
** Command puns
|
||||
|
||||
For more easily running ~Cmd~s, we now have the following syntactic puns.
|
||||
|
||||
#+begin_src julia
|
||||
~ `cmd` # read to string
|
||||
! `cmd` # print output
|
||||
!~ `cmd` # read and print output
|
||||
`a` | `b` # pipe commands
|
||||
#+end_src
|
||||
|
||||
** Package configuration
|
||||
|
||||
Thanks to package extensions, it's fairly straightforward to bundle per-package
|
||||
customisation code. Currently this covers the following:
|
||||
|
||||
+ Setting the Makie theme based on the terminal colours when loading =CairoMakie=
|
||||
+ Pushing the =Gadfly= display
|
||||
+ Changing the =HDF5= icons
|
||||
+ Loading =ImageShow= when =JpegTurbo=, =Netpbm=, =PNGFiles=, =QOI=, or =TiffImages= are loaded
|
||||
+ Load an appropriate =OhMyREPL= theme on startup
|
||||
+ Add pretty colour show methods for =Color= types
|
|
@ -0,0 +1,102 @@
|
|||
module cairomakie
|
||||
|
||||
using Setup
|
||||
using CairoMakie
|
||||
import Pkg
|
||||
|
||||
import Setup.theme_term
|
||||
|
||||
import CairoMakie.Colors.FixedPointNumbers.N0f8
|
||||
import CairoMakie.Colors.RGB
|
||||
|
||||
import CairoMakie.Makie: Theme, RGBAf, Attributes, Axis, Axis3, Legend, Colorbar
|
||||
|
||||
function theme_term()
|
||||
termcolors = Setup.termcolors()
|
||||
|
||||
if isempty(termcolors)
|
||||
return CairoMakie.Makie.current_default_theme()
|
||||
end
|
||||
|
||||
function tocolor(name::Symbol)
|
||||
rgb = get(termcolors, name, (r=0x00, g=0x00, b=0x00))
|
||||
RGB(reinterpret(N0f8, rgb.r),
|
||||
reinterpret(N0f8, rgb.g),
|
||||
reinterpret(N0f8, rgb.b))
|
||||
end
|
||||
|
||||
bg, fg, red, green, yellow, blue, magenta, cyan,
|
||||
lbg, lfg, lred, lgreen, lyellow, lblue, lmagenta, lcyan =
|
||||
tocolor.((:black, :white, :red, :green, :yellow, :blue,
|
||||
:magenta, :cyan, :light_black, :light_white,
|
||||
:light_red, :light_green, :light_yellow,
|
||||
:light_blue, :light_magenta, :light_cyan))
|
||||
|
||||
onecolors(α = 1.0) =
|
||||
RGBAf.([blue, yellow, green, magenta, red, cyan, lblue,
|
||||
lyellow, lgreen, lmagenta, lred, lcyan],
|
||||
α)
|
||||
|
||||
Theme(
|
||||
backgroundcolor = bg,
|
||||
textcolor = fg,
|
||||
linecolor = fg,
|
||||
font = :regular,
|
||||
fonts = Attributes(
|
||||
bold = "JetBrains Mono Bold",
|
||||
bold_italic = "JetBrains Mono Bold Italic",
|
||||
italic = "JetBrains Mono Italic",
|
||||
regular = "JetBrains Mono Medium"),
|
||||
palette = merge(Attributes(
|
||||
color = onecolors(1.0),
|
||||
patchcolor = onecolors(0.8)
|
||||
), CairoMakie.Makie.default_palettes),
|
||||
Axis = (
|
||||
backgroundcolor = :transparent,
|
||||
bottomspinecolor = fg,
|
||||
topspinecolor = fg,
|
||||
leftspinecolor = fg,
|
||||
rightspinecolor = fg,
|
||||
xgridcolor = RGBAf(1, 1, 1, 0.16),
|
||||
ygridcolor = RGBAf(1, 1, 1, 0.16),
|
||||
xtickcolor = fg,
|
||||
ytickcolor = fg),
|
||||
Legend = (
|
||||
framecolor = fg,
|
||||
bgcolor = bg),
|
||||
Axis3 = (
|
||||
xgridcolor = RGBAf(1, 1, 1, 0.16),
|
||||
ygridcolor = RGBAf(1, 1, 1, 0.16),
|
||||
zgridcolor = RGBAf(1, 1, 1, 0.16),
|
||||
xspinecolor_1 = fg,
|
||||
yspinecolor_1 = fg,
|
||||
zspinecolor_1 = fg,
|
||||
xspinecolor_2 = fg,
|
||||
yspinecolor_2 = fg,
|
||||
zspinecolor_2 = fg,
|
||||
xspinecolor_3 = fg,
|
||||
yspinecolor_3 = fg,
|
||||
zspinecolor_3 = fg,
|
||||
xticklabelpad = 3,
|
||||
yticklabelpad = 3,
|
||||
zticklabelpad = 6,
|
||||
xtickcolor = fg,
|
||||
ytickcolor = fg,
|
||||
ztickcolor = fg),
|
||||
Colorbar = (
|
||||
tickcolor = fg,
|
||||
spinecolor = fg,
|
||||
topspinecolor = fg,
|
||||
bottomspinecolor = fg,
|
||||
leftspinecolor = fg,
|
||||
rightspinecolor = fg))
|
||||
end
|
||||
|
||||
function __init__()
|
||||
if isinteractive()
|
||||
Setup.load_termimage_pkg()
|
||||
CairoMakie.set_theme!(theme_term())
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
module gadfly
|
||||
|
||||
using Gadfly
|
||||
|
||||
function __init__()
|
||||
pushdisplay(Gadfly.GadflyDisplay())
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,22 @@
|
|||
module hdf5
|
||||
|
||||
using HDF5
|
||||
|
||||
import HDF5._tree_icon
|
||||
|
||||
function __init__()
|
||||
_tree_icon(::Type{HDF5.Attribute}) =
|
||||
HDF5.SHOW_TREE_ICONS[] ? "\e[33m\e[39m" : "[A]"
|
||||
_tree_icon(::Type{HDF5.Group}) =
|
||||
HDF5.SHOW_TREE_ICONS[] ? "\e[34m\e[39m" : "[G]"
|
||||
_tree_icon(::Type{HDF5.Dataset}) =
|
||||
HDF5.SHOW_TREE_ICONS[] ? "\e[35m\e[39m" : "[D]"
|
||||
_tree_icon(::Type{HDF5.Datatype}) =
|
||||
HDF5.SHOW_TREE_ICONS[] ? "\e[36m\e[39m" : "[T]"
|
||||
_tree_icon(::Type{HDF5.File}) =
|
||||
HDF5.SHOW_TREE_ICONS[] ? "\e[34m\e[39m" : "[F]"
|
||||
_tree_icon(::Type) =
|
||||
HDF5.SHOW_TREE_ICONS[] ? "\e[31m❓\e[39m" : "[?]"
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
module image_jpeg
|
||||
include("images.jl")
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
module image_netpbm
|
||||
include("images.jl")
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
module image_png
|
||||
include("images.jl")
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
module image_qoi
|
||||
include("images.jl")
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
module image_tiff
|
||||
include("images.jl")
|
||||
end
|
|
@ -0,0 +1,29 @@
|
|||
# This is loaded by all other `image_*.jl` files
|
||||
|
||||
using Setup
|
||||
|
||||
const IMAGE_SHOW_PKG, IMAGE_IO_PKG, IMAGE_MAGICK_PKG = splat(Base.PkgId).([
|
||||
(Base.UUID("4e3cecfd-b093-5904-9786-8bbb286a6a31"), "ImageShow"),
|
||||
(Base.UUID("82e4d734-157c-48bb-816b-45c225c6df19"), "ImageIO"),
|
||||
(Base.UUID("6218d12a-5da1-5696-b52f-db25d2ecc6d1"), "ImageMagick")])
|
||||
|
||||
function __init__()
|
||||
if isinteractive()
|
||||
Setup.load_termimage_pkg()
|
||||
if !haskey(Base.loaded_modules, IMAGE_SHOW_PKG) &&
|
||||
!isnothing(Base.find_package(IMAGE_SHOW_PKG.name)) &&
|
||||
(!isnothing(Base.find_package(IMAGE_IO_PKG.name)) ||
|
||||
!isnothing(Base.find_package(IMAGE_MAGICK_PKG.name)))
|
||||
# Don't to this async because it may affect a `display` call
|
||||
# that's just about to occur.
|
||||
if !isnothing(Base.find_package(IMAGE_IO_PKG.name))
|
||||
Base.require(IMAGE_IO_PKG)
|
||||
elseif !isnothing(Base.find_package(IMAGE_MAGICK_PKG.name))
|
||||
Base.require(IMAGE_MAGICK_PKG)
|
||||
else
|
||||
return
|
||||
end
|
||||
Base.require(IMAGE_SHOW_PKG)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,28 @@
|
|||
module ohmyrepl
|
||||
|
||||
using Setup
|
||||
using OhMyREPL
|
||||
|
||||
function __init__()
|
||||
theme = Setup.termtheme()
|
||||
if theme == :dark
|
||||
OhMyREPL.colorscheme!("OneDark")
|
||||
elseif theme == :light
|
||||
OhMyREPL.colorscheme!("TomorrowDay")
|
||||
else theme == :unknown
|
||||
OhMyREPL.colorscheme!("Monokai16")
|
||||
end
|
||||
|
||||
if Setup.TERM[] == "xterm-kitty"
|
||||
OhMyREPL.input_prompt!(:green) do
|
||||
print("\e]133;A\e\\")
|
||||
"julia> "
|
||||
end
|
||||
OhMyREPL.output_prompt!(:red) do
|
||||
print("\e]133;C\e\\")
|
||||
""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,58 @@
|
|||
module prettycolors
|
||||
|
||||
using Colors
|
||||
using LinearAlgebra # for the Adjoint type
|
||||
|
||||
import Base.show
|
||||
|
||||
colorcode(rgb::Vector{<:Integer}, background=false) =
|
||||
string("\e[", if background "48" else "38" end,
|
||||
";2;", rgb[1], ";", rgb[2], ";", rgb[3], "m")
|
||||
|
||||
colorcode(c::Union{RGB, RGB24}, background=false) =
|
||||
colorcode(reinterpret.([red(c), green(c), blue(c)]), background)
|
||||
|
||||
colorcode(c::RGB{Float64}, background=false) =
|
||||
colorcode(round.(Int, 255 .* [red(c), green(c), blue(c)]), background)
|
||||
|
||||
colorcode(c::Colorant, background=false) =
|
||||
colorcode(convert(RGB24, c), background)
|
||||
|
||||
function show(io::IO, ::MIME"text/plain", c::Colorant)
|
||||
if get(io, :color, false) && get(ENV, "COLORTERM", nothing) == "truecolor"
|
||||
print(colorcode(c, true), " \e[0m ")
|
||||
end
|
||||
show(io, c)
|
||||
end
|
||||
|
||||
function show(io::IO, ::MIME"text/plain", cs::Adjoint{<:Colorant, <:Vector{<:Colorant}})
|
||||
summary(io, cs)
|
||||
print(":\n")
|
||||
if get(io, :color, false) && get(ENV, "COLORTERM", nothing) == "truecolor"
|
||||
print(" ", join(colorcode.(cs,true), if length(cs) <= 40 " " else " " end),
|
||||
if length(cs) <= 40 " \e[0m" else " \e[0m" end)
|
||||
else
|
||||
Base.print_array(IOContext(io, :SHOWN_SET => cs), cs)
|
||||
end
|
||||
end
|
||||
|
||||
function show(io::IO, ::MIME"text/plain", cs::Matrix{<:Colorant})
|
||||
summary(io, cs)
|
||||
print(":\n")
|
||||
if get(io, :color, false) && *(size(cs)...) <= 1000 && get(ENV, "COLORTERM", nothing) == "truecolor"
|
||||
for csrow in eachrow(cs)
|
||||
println(" ", join(colorcode.(csrow,true),
|
||||
if size(cs,2) <= 40 " " else " " end),
|
||||
if size(cs,2) <= 40 " \e[0m" else " \e[0m" end)
|
||||
end
|
||||
else
|
||||
Base.print_array(IOContext(io, :SHOWN_SET => cs), cs)
|
||||
end
|
||||
end
|
||||
|
||||
function Base.display(c::Colorant)
|
||||
show(stdout, MIME("text/plain"), c)
|
||||
print('\n')
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,80 @@
|
|||
module Setup
|
||||
|
||||
import REPL
|
||||
import Pkg
|
||||
|
||||
export about, @about
|
||||
|
||||
@static if VERSION >= v"1.8"
|
||||
TERM::Ref{String} = get(ENV, "TERM", "")
|
||||
IS_SSH::Ref{Bool} = Ref(false)
|
||||
else
|
||||
TERM = get(ENV, "TERM", "")
|
||||
IS_SSH = Ref(false)
|
||||
end
|
||||
|
||||
include("install.jl")
|
||||
|
||||
include("cmdpuns.jl")
|
||||
include("termsetup.jl")
|
||||
include("about.jl")
|
||||
include("pkgstack.jl")
|
||||
|
||||
include("autoloads.jl")
|
||||
using .Autoloads
|
||||
include("autoload_data.jl")
|
||||
|
||||
include("sessions.jl")
|
||||
using .Sessions
|
||||
|
||||
using .PkgStack
|
||||
|
||||
function ensurepkg(pkg::String)
|
||||
if isnothing(Base.find_package(pkg))
|
||||
@info "Installing $pkg"
|
||||
Pkg.add(pkg)
|
||||
end
|
||||
end
|
||||
|
||||
function theme_term end # For cairomakie.jl
|
||||
|
||||
function __init__()
|
||||
IS_SSH[] = haskey(ENV, "SSH_CLIENT") || haskey(ENV, "SSH_TTY")
|
||||
if isinteractive()
|
||||
TERM[] = termcode()
|
||||
|
||||
Main.Threads.@spawn if TERM[] == "xterm-kitty"
|
||||
if isnothing(Base.find_package("KittyTerminalImages"))
|
||||
@info "Installing KittyTerminalImages"
|
||||
currentproj = Pkg.project().path
|
||||
try
|
||||
Pkg.activate()
|
||||
# Pkg.add("KittyTerminalImages")
|
||||
finally
|
||||
Pkg.activate(currentproj)
|
||||
end
|
||||
end
|
||||
termtitle(pwd())
|
||||
end
|
||||
|
||||
if ENV["TERM"] != "dumb"
|
||||
replsetup()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
precompile(termtitle, ())
|
||||
precompile(replsetup, ())
|
||||
precompile(_termcolors, ())
|
||||
precompile(termcolors, ())
|
||||
precompile(termtheme, ())
|
||||
|
||||
precompile(Autoloads.autoload_loadpkg, (Symbol,))
|
||||
precompile(Autoloads.autoload_scan!, (Expr,))
|
||||
precompile(Autoloads.autoload_scan!, (Symbol,))
|
||||
precompile(Autoloads.autoload_scan!, (Any,))
|
||||
precompile(Autoloads.read_named_envs!, ())
|
||||
precompile(Autoloads.repl_scan_autoloads!, (Expr,))
|
||||
precompile(Autoloads.repl_scan_autoloads!, (Nothing,))
|
||||
|
||||
end
|
|
@ -0,0 +1,256 @@
|
|||
using InteractiveUtils
|
||||
|
||||
const colorcycle = [:light_blue, :light_green, :light_yellow, :light_magenta]
|
||||
|
||||
function humansize(bytes::Integer)
|
||||
units = ("B", "kB", "MB", "GB")
|
||||
magnitude = floor(Int, log(1024, 1 + bytes))
|
||||
if 10 < bytes < 10*1024^magnitude
|
||||
round(bytes / 1024^magnitude, digits=1)
|
||||
else
|
||||
round(Int, bytes / 1024^magnitude)
|
||||
end, units[1+magnitude]
|
||||
end
|
||||
|
||||
function about(obj::Any)
|
||||
print(Base.summary(obj))
|
||||
size, units = humansize(Base.summarysize(obj))
|
||||
if ismutable(obj)
|
||||
print(" (mutable)")
|
||||
end
|
||||
print(", ", size, units, "\n\n")
|
||||
printstyled("Fields:\n", bold=true)
|
||||
memorylayout(obj)
|
||||
end
|
||||
|
||||
function about(type::Type)
|
||||
if isprimitivetype(type)
|
||||
print("Primitive ")
|
||||
elseif isconcretetype(type)
|
||||
print("Concrete ")
|
||||
if Base.datatype_haspadding(type)
|
||||
printstyled("(padded) ", color=:light_black)
|
||||
end
|
||||
elseif isabstracttype(type)
|
||||
print("Abstract ")
|
||||
end
|
||||
if Base.issingletontype(type)
|
||||
print("singleton ")
|
||||
end
|
||||
print(Base.summary(type))
|
||||
print(" defined in ")
|
||||
printstyled(type.name.module, color=:light_red)
|
||||
size, units = humansize(Base.summarysize(type))
|
||||
selfsize, selfunits = try
|
||||
st = humansize(sizeof(type))
|
||||
print(", ", st...)
|
||||
st
|
||||
catch _; (0, "") end
|
||||
if (size, units) != (selfsize, selfunits)
|
||||
print(" referencing ", size, units)
|
||||
end
|
||||
println("\n ", join(string.(supertypes(type)), " \e[31m<:\e[m "))
|
||||
if isstructtype(type) && fieldcount(type) > 0
|
||||
println("\nStruct with \e[1m", fieldcount(type), "\e[m fields:")
|
||||
if type isa DataType
|
||||
for (i, (fname, ftype)) in enumerate(zip(fieldnames(type), fieldtypes(type)))
|
||||
color = colorcycle[i % length(colorcycle) + 1]
|
||||
print(" • ")
|
||||
printstyled(fname; color)
|
||||
printstyled("::", string(ftype), color=:light_black)
|
||||
print('\n')
|
||||
end
|
||||
print('\n')
|
||||
memorylayout(type)
|
||||
else
|
||||
println(" • ",
|
||||
join(string.(fieldnames(type), "\e[90m::",
|
||||
fieldtypes(type), "\e[m"),
|
||||
"\n • "))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function structinfo(T::DataType)
|
||||
map(1:fieldcount(T)) do i
|
||||
size = Int(if i < fieldcount(T) fieldoffset(T, i+1)
|
||||
else sizeof(T) end - fieldoffset(T, i))
|
||||
contentsize=try sizeof(fieldtype(T, i)) catch _ 0 end
|
||||
(; i, offset=fieldoffset(T, i) |> Int,
|
||||
size, contentsize,
|
||||
pointer=contentsize == 0,
|
||||
name=fieldname(T, i),
|
||||
type=fieldtype(T, i))
|
||||
end
|
||||
end
|
||||
|
||||
function memorylayout(type::DataType)
|
||||
cpad(s, n::Integer, p=" ") = rpad(lpad(s,div(n+textwidth(s),2),p),n,p)
|
||||
si = structinfo(type)
|
||||
!isempty(si) || return
|
||||
memstep = memstep = gcd((getfield.(si, :size), getfield.(si, :contentsize)) |>
|
||||
Iterators.flatten |> collect)
|
||||
memscale = max(1, floor(Int, 70/(sizeof(type)/memstep)))
|
||||
print(' ')
|
||||
for (; i, size, contentsize, pointer) in si
|
||||
color = colorcycle[i % length(colorcycle) + 1]
|
||||
contentwidth = memscale * contentsize÷memstep
|
||||
printstyled('■'^contentwidth; color)
|
||||
if contentsize < size
|
||||
paddwidth = memscale * (size - contentsize)÷memstep
|
||||
printstyled('■'^paddwidth;
|
||||
color=if pointer; :cyan else :light_black end)
|
||||
end
|
||||
end
|
||||
print("\n ")
|
||||
for (; i, size, contentsize, pointer) in si
|
||||
width = memscale * size÷memstep
|
||||
color = colorcycle[i % length(colorcycle) + 1]
|
||||
fsize, funits = humansize(size)
|
||||
if pointer
|
||||
printstyled(cpad("*", width); color)
|
||||
elseif contentsize < size
|
||||
csize, cunits = humansize(contentsize)
|
||||
psize, punits = humansize(size - contentsize)
|
||||
csizestr, psizestr = string(csize, cunits), string(psize, punits)
|
||||
cwidth = div(width + textwidth(csizestr) - textwidth(psizestr) - 1, 2)
|
||||
pwidth = width - cwidth
|
||||
printstyled(lpad(csizestr, cwidth); color)
|
||||
printstyled(rpad('+' * psizestr, pwidth); color=:light_black)
|
||||
else
|
||||
printstyled(cpad(string(fsize, funits), width); color)
|
||||
end
|
||||
end
|
||||
if any(getfield.(si, :pointer))
|
||||
printstyled("\n\n * = Pointer (8B)", color=:cyan)
|
||||
end
|
||||
print('\n')
|
||||
end
|
||||
|
||||
function memorylayout(val::T) where {T}
|
||||
si = structinfo(T)
|
||||
!isempty(si) || return memorylayout(T)
|
||||
rows = Vector{NamedTuple}()
|
||||
for (; name, size, contentsize, pointer, type) in si
|
||||
fsize, funits = humansize(size)
|
||||
fbits = if isprimitivetype(type)
|
||||
bitstring(getfield(val, name)) * '-'^(8*(size-contentsize))
|
||||
elseif pointer
|
||||
"Pointer"
|
||||
else
|
||||
""
|
||||
end
|
||||
fshow = sprint(show, getfield(val, name), context=:compact => true)
|
||||
push!(rows, (; fname=string(name), fsize=string(fsize), funits,
|
||||
fbits, fshow, ftype=string(type)))
|
||||
end
|
||||
widths = Dict(c => maximum(length.(getfield.(rows, c)))
|
||||
for c in fieldnames(typeof(first(rows)))) |>
|
||||
NamedTuple
|
||||
for (i, (; fname, fsize, funits, fbits, fshow, ftype)) in enumerate(rows)
|
||||
color = colorcycle[i % length(colorcycle) + 1]
|
||||
printstyled(' ', lpad(fname, widths.fname), "::",
|
||||
rpad(ftype, widths.ftype); color)
|
||||
printstyled(" ", lpad(fsize, widths.fsize),
|
||||
rpad(funits, widths.funits), ' '; color)
|
||||
print(replace(rpad(fbits, widths.fbits), r"0|\-" => s"\e[90m\0\e[m"), ' ')
|
||||
printstyled(rpad(fshow, widths.fshow); color)
|
||||
print('\n')
|
||||
end
|
||||
print('\n')
|
||||
memorylayout(T)
|
||||
end
|
||||
|
||||
function about(fn::Function)
|
||||
source = Main.InteractiveUtils.which(Main, Symbol(fn))
|
||||
methodmodules = getproperty.(methods(fn).ms, :module)
|
||||
others = setdiff(methodmodules, [source])
|
||||
print(Base.summary(fn))
|
||||
print("\n\nDefined in ")
|
||||
printstyled(source, color=:light_red)
|
||||
if length(others) > 0
|
||||
printstyled("(", sum(Ref(source) .=== methodmodules), ")",
|
||||
color=:light_black)
|
||||
print(" extended in ")
|
||||
for (i, oth) in enumerate(others)
|
||||
printstyled(oth, color=:light_red)
|
||||
printstyled("(", sum(Ref(oth) .=== methodmodules), ")",
|
||||
color=:light_black)
|
||||
if length(others) == 2 && i == 1
|
||||
print(" and ")
|
||||
elseif length(others) > 2 && i < length(others)-1
|
||||
print(", ")
|
||||
elseif length(others) > 2 && i == length(others)-1
|
||||
print(", and ")
|
||||
end
|
||||
end
|
||||
end
|
||||
print(".\n")
|
||||
end
|
||||
|
||||
function about(fn::Union{Function, Type}, typesig::Type{<:Tuple})
|
||||
about(Base.unwrap_unionall(fn))
|
||||
print("\n")
|
||||
matchedmethods = methods(fn, typesig)
|
||||
printstyled("Matching methods ($(length(matchedmethods.ms))):\n", bold=true)
|
||||
for method in matchedmethods.ms
|
||||
println(" ", sprint(show, method, context=IOContext(stdout)))
|
||||
end
|
||||
print('\n')
|
||||
printstyled("Effects:\n", bold=true)
|
||||
effects = Base.infer_effects(fn, typesig)
|
||||
trichar(t::UInt8) =
|
||||
Dict(Core.Compiler.ALWAYS_TRUE => '✔',
|
||||
# Core.Compiler.TRISTATE_UNKNOWN => '?',
|
||||
Core.Compiler.ALWAYS_FALSE => '✗')[t]
|
||||
trichar(b::Bool) = ifelse(b, '✔', '✗')
|
||||
tricolor(t::UInt8) =
|
||||
Dict(Core.Compiler.ALWAYS_TRUE => :green,
|
||||
# Core.Compiler.TRISTATE_UNKNOWN => :yellow,
|
||||
Core.Compiler.ALWAYS_FALSE => :red)[t]
|
||||
tricolor(b::Bool) = ifelse(b, :green, :red)
|
||||
hedge(t::UInt8) =
|
||||
Dict(Core.Compiler.ALWAYS_TRUE => "guaranteed to",
|
||||
# Core.Compiler.TRISTATE_UNKNOWN => "might",
|
||||
Core.Compiler.ALWAYS_FALSE => "not guaranteed to")[t]
|
||||
hedge(b::Bool) = ifelse(b, "guaranteed to", "not guaranteed to")
|
||||
for (effect, description) in
|
||||
[(:consistent, "return or terminate consistently"),
|
||||
(:effect_free, "be free from externally semantically visible side effects"),
|
||||
(:nothrow, "never throw an exception"),
|
||||
(:terminates, "terminate")]
|
||||
e = getfield(effects, effect)
|
||||
print(" "); printstyled(trichar(e), ' ', string(effect), color=tricolor(e))
|
||||
print(" "); printstyled(hedge(e), ' ', description, color=:light_black);
|
||||
print('\n')
|
||||
end
|
||||
if !effects.nonoverlayed
|
||||
print(" "); printstyled(trichar(Core.Compiler.ALWAYS_FALSE), " nonoverlayed",
|
||||
color=tricolor(Core.Compiler.ALWAYS_FALSE))
|
||||
print(" "); printstyled("methods within this method are not defined in an overlayed method table",
|
||||
color=:light_black);
|
||||
print('\n')
|
||||
end
|
||||
end
|
||||
|
||||
macro about(obj::Symbol, types::Union{Symbol, Expr}...)
|
||||
if Core.eval(Main, obj) isa Function && length(types) == 1 &&
|
||||
types[1] == :(())
|
||||
:(about($obj, Tuple{}))
|
||||
elseif Core.eval(Main, obj) isa Function && length(types) > 0
|
||||
:(about($obj, $(Tuple{Core.eval.(Main, types)...})))
|
||||
else
|
||||
:(about($obj))
|
||||
end |> esc
|
||||
end
|
||||
|
||||
macro about(qobj::Union{QuoteNode, Expr}, types::Union{Symbol, Expr}...)
|
||||
if Core.eval(Main, Core.eval(Main, qobj)) isa Function && length(types) == 1 &&
|
||||
types[1] == :(())
|
||||
:(about($(Core.eval(Main, qobj)), Tuple{}))
|
||||
elseif length(types) > 0
|
||||
:(about($(Core.eval(Main, qobj)), $(Tuple{Core.eval.(Main, types)...})))
|
||||
else
|
||||
:(about($(Core.eval(Main, qobj))))
|
||||
end |> esc
|
||||
end
|
|
@ -0,0 +1,258 @@
|
|||
foreach(add_autoloads!, [
|
||||
PkgAutoloads(
|
||||
:(Base.Threads),
|
||||
exported = Bindings(
|
||||
macros = ["@spawn", "@threads"],
|
||||
callables = [
|
||||
:Atomic, :Event, :SpinLock, :Threads, :atomic_add!,
|
||||
:atomic_and!, :atomic_cas!, :atomic_fence, :atomic_max!,
|
||||
:atomic_min!, :atomic_nand!, :atomic_or!, :atomic_sub!,
|
||||
:atomic_xchg!, :atomic_xor!, :nthreadpools, :nthreads,
|
||||
:threadid, :threadpool])),
|
||||
PkgAutoloads(:Base64, exported = Bindings(callables = [
|
||||
:Base64DecodePipe, :Base64EncodePipe, :base64decode, :base64encode,
|
||||
:stringmime])),
|
||||
PkgAutoloads(:BaseDirs),
|
||||
PkgAutoloads(
|
||||
:BenchmarkTools,
|
||||
exported = Bindings(macros = [
|
||||
"@ballocated", "@belapsed", "@benchmark", "@benchmarkable",
|
||||
"@benchmarkset", "@bprofile", "@btime", "@case", "@tagged"])),
|
||||
PkgAutoloads(:CRC32c, exported = Bindings(callables = [:crc32c])),
|
||||
PkgAutoloads(:CSV),
|
||||
PkgAutoloads(:Cthulhu, exported = Bindings(
|
||||
macros = ["@decend", "@decend_code_typed", "@decend_code_warntype"],
|
||||
callables = [:ascend, :descend, :descend_code_typed, :descend_code_warntype])),
|
||||
PkgAutoloads(
|
||||
:DataFrames,
|
||||
exported = Bindings(callables = [
|
||||
:AbstractDataFrame, :All, :AsTable, :Between, :ByRow, :Cols,
|
||||
:DataFrame, :DataFrameRow, :DataFrames, :GroupedDataFrame,
|
||||
:InvertedIndex, :InvertedIndices, :Missing, :MissingException,
|
||||
:Missings, :Not, :PrettyTables, :SubDataFrame, :Tables, :aggregate,
|
||||
:allcombinations, :allowmissing, :allowmissing!, :antijoin, :by,
|
||||
:coalesce, :colmetadata, :colmetadata!, :colmetadatakeys,
|
||||
:columnindex, :combine, :completecases, :crossjoin, :delete!,
|
||||
:deletecolmetadata!, :deletemetadata!, :disallowmissing,
|
||||
:disallowmissing!, :dropmissing, :dropmissing!, :emptycolmetadata!,
|
||||
:emptymetadata!, :emptymissing, :fillcombinations, :flatten,
|
||||
:groupby, :groupcols, :groupindices, :innerjoin, :insertcols,
|
||||
:insertcols!, :ismissing, :leftjoin, :leftjoin!, :levels, :mapcols,
|
||||
:mapcols!, :metadata, :metadata!, :metadatakeys, :missing,
|
||||
:missings, :ncol, :nonmissingtype, :nonunique, :nrow, :order,
|
||||
:outerjoin, :passmissing, :proprow, :rename, :rename!, :repeat!,
|
||||
:rightjoin, :rownumber, :select, :select!, :semijoin, :skipmissings,
|
||||
:subset, :subset!, :transform, :transform!, :unique!, :unstack,
|
||||
:valuecols])),
|
||||
PkgAutoloads(
|
||||
:DataToolkit,
|
||||
exported = Bindings(
|
||||
macros = ["@d_str", "@data_cmd", "@import"],
|
||||
callables = [:DataSet, :DataToolkit, :dataset, :loadcollection!])),
|
||||
PkgAutoloads(
|
||||
:Dates,
|
||||
exported = Bindings(
|
||||
macros = ["@dateformat_str"],
|
||||
callables = [
|
||||
:Apr, :April, :Aug, :August, :Date, :DateFormat, :DatePeriod,
|
||||
:DateTime, :Dates, :Day, :Dec, :December, :Feb, :February, :Fri,
|
||||
:Friday, :Hour, :ISODateFormat, :ISODateTimeFormat,
|
||||
:ISOTimeFormat, :Jan, :January, :Jul, :July, :Jun, :June, :Mar,
|
||||
:March, :May, :Microsecond, :Millisecond, :Minute, :Mon,
|
||||
:Monday, :Month, :Nanosecond, :Nov, :November, :Oct, :October,
|
||||
:Period, :Quarter, :RFC1123Format, :Sat, :Saturday, :Second,
|
||||
:Sep, :September, :Sun, :Sunday, :Thu, :Thursday, :Time,
|
||||
:TimePeriod, :TimeType, :TimeZone, :Tue, :Tuesday, :UTC, :Wed,
|
||||
:Wednesday, :Week, :Year, :adjust, :canonicalize,
|
||||
:datetime2julian, :datetime2rata, :datetime2unix, :day,
|
||||
:dayabbr, :dayname, :dayofmonth, :dayofquarter, :dayofweek,
|
||||
:dayofweekofmonth, :dayofyear, :daysinmonth, :daysinyear,
|
||||
:daysofweekinmonth, :firstdayofmonth, :firstdayofquarter,
|
||||
:firstdayofweek, :firstdayofyear, :hour, :isleapyear,
|
||||
:julian2datetime, :lastdayofmonth, :lastdayofquarter,
|
||||
:lastdayofweek, :lastdayofyear, :microsecond, :millisecond,
|
||||
:minute, :month, :monthabbr, :monthday, :monthname, :nanosecond,
|
||||
:now, :quarterofyear, :rata2datetime, :second, :today, :tofirst,
|
||||
:tolast, :tonext, :toprev, :unix2datetime, :week, :year,
|
||||
:yearmonth, :yearmonthday])),
|
||||
PkgAutoloads(:Debugger, exported = Bindings(macros = ["@enter", "@run"])),
|
||||
PkgAutoloads(
|
||||
:Distributed,
|
||||
exported = Bindings(
|
||||
macros = ["@distributed", "@everywhere", "@fetch", "@fetchfrom", "@spawnat"],
|
||||
callables = [
|
||||
:AbstractWorkerPool, :CachingPool, :ClusterManager, :Future,
|
||||
:ProcessExitedException, :RemoteChannel, :RemoteException,
|
||||
:WorkerConfig, :WorkerPool, :addprocs, :channel_from_id,
|
||||
:check_same_host, :clear!, :cluster_cookie,
|
||||
:default_worker_pool, :init_worker, :interrupt, :launch,
|
||||
:manage, :myid, :nprocs, :nworkers, :pmap, :process_messages,
|
||||
:procs, :remote, :remote_do, :remotecall, :remotecall_fetch,
|
||||
:remotecall_wait, :remoteref_id, :rmprocs, :start_worker,
|
||||
:worker_id_from_socket, :workers])),
|
||||
PkgAutoloads(
|
||||
:Distributions,
|
||||
exported = Bindings(callables = [
|
||||
:AbstractMixtureModel, :AbstractMvNormal, :Arcsine,
|
||||
:ArrayLikeVariate, :Bernoulli, :BernoulliLogit, :Beta,
|
||||
:BetaBinomial, :BetaPrime, :Binomial, :Biweight, :Categorical,
|
||||
:Cauchy, :Chernoff, :Chi, :Chisq, :CholeskyVariate, :Continuous,
|
||||
:ContinuousDistribution, :ContinuousMatrixDistribution,
|
||||
:ContinuousMultivariateDistribution,
|
||||
:ContinuousUnivariateDistribution, :Cosine, :DiagNormal,
|
||||
:DiagNormalCanon, :Dirac, :Dirichlet, :DirichletMultinomial,
|
||||
:Discrete, :DiscreteDistribution, :DiscreteMatrixDistribution,
|
||||
:DiscreteMultivariateDistribution, :DiscreteNonParametric,
|
||||
:DiscreteUniform, :DiscreteUnivariateDistribution, :Distribution,
|
||||
:DoubleExponential, :EdgeworthMean, :EdgeworthSum, :EdgeworthZ,
|
||||
:Epanechnikov, :Erlang, :Estimator, :Exponential, :FDist,
|
||||
:FisherNoncentralHypergeometric, :Frechet, :FullNormal,
|
||||
:FullNormalCanon, :Gamma, :GeneralizedExtremeValue,
|
||||
:GeneralizedPareto, :Geometric, :Gumbel, :Hypergeometric,
|
||||
:InverseGamma, :InverseGaussian, :InverseWishart, :IsoNormal,
|
||||
:IsoNormalCanon, :JohnsonSU, :JointOrderStatistics, :KSDist,
|
||||
:KSOneSided, :Kolmogorov, :Kumaraswamy, :LKJ, :LKJCholesky,
|
||||
:Laplace, :Levy, :Lindley, :LocationScale, :LogNormal, :LogUniform,
|
||||
:Logistic, :LogitNormal, :MLEstimator, :MatrixBeta,
|
||||
:MatrixDistribution, :MatrixFDist, :MatrixNormal, :MatrixReshaped,
|
||||
:MatrixTDist, :Matrixvariate, :MixtureModel, :Multinomial,
|
||||
:Multivariate, :MultivariateDistribution, :MultivariateMixture,
|
||||
:MultivariateNormal, :MvLogNormal, :MvNormal, :MvNormalCanon,
|
||||
:MvNormalKnownCov, :MvTDist, :NegativeBinomial,
|
||||
:NonMatrixDistribution, :NoncentralBeta, :NoncentralChisq,
|
||||
:NoncentralF, :NoncentralHypergeometric, :NoncentralT, :Normal,
|
||||
:NormalCanon, :NormalInverseGaussian, :OrderStatistic,
|
||||
:PGeneralizedGaussian, :Pareto, :Poisson, :PoissonBinomial,
|
||||
:Product, :QQPair, :Rayleigh, :RealInterval, :Rician, :Sampleable,
|
||||
:Semicircle, :Skellam, :SkewNormal, :SkewedExponentialPower,
|
||||
:Soliton, :StudentizedRange, :SufficientStats, :SymTriangularDist,
|
||||
:TDist, :TriangularDist, :Triweight, :Truncated, :TruncatedNormal,
|
||||
:Uniform, :Univariate, :UnivariateDistribution, :UnivariateGMM,
|
||||
:UnivariateMixture, :ValueSupport, :VariateForm, :VonMises,
|
||||
:VonMisesFisher, :WalleniusNoncentralHypergeometric, :Weibull,
|
||||
:Wishart, :ZeroMeanDiagNormal, :ZeroMeanDiagNormalCanon,
|
||||
:ZeroMeanFullNormal, :ZeroMeanFullNormalCanon, :ZeroMeanIsoNormal,
|
||||
:ZeroMeanIsoNormalCanon, :canonform, :ccdf, :cdf, :censored, :cf,
|
||||
:cgf, :circvar, :component, :components, :componentwise_logpdf,
|
||||
:componentwise_pdf, :concentration, :convolve, :cquantile,
|
||||
:estimate, :expected_logdet, :failprob, :fit_mle, :gradlogpdf,
|
||||
:hasfinitesupport, :insupport, :invcov, :invlogccdf, :invlogcdf,
|
||||
:invscale, :isbounded, :isleptokurtic, :islowerbounded,
|
||||
:ismesokurtic, :isplatykurtic, :isprobvec, :isupperbounded,
|
||||
:location, :location!, :logccdf, :logcdf, :logdetcov, :logdiffcdf,
|
||||
:logpdf, :logpdf!, :meandir, :meanform, :meanlogx, :mgf,
|
||||
:ncategories, :ncomponents, :nsamples, :ntrials, :partype, :pdf,
|
||||
:pdfsquaredL2norm, :probs, :probval, :product_distribution,
|
||||
:qqbuild, :rate, :sampler, :scale, :scale!, :shape, :sqmahal,
|
||||
:sqmahal!, :stdlogx, :succprob, :suffstats, :support, :truncated,
|
||||
:varlogx])),
|
||||
PkgAutoloads(
|
||||
:Downloads,
|
||||
exported = Bindings(callables = [
|
||||
:Downloader, :RequestError, :Response, :download, :request])),
|
||||
PkgAutoloads(:JET, exported = Bindings(
|
||||
macros = ["@report_call", "@report_opt", "@test_call", "@test_opt"],
|
||||
callables = [:AnyFrameModule, :LastFrameModule, :report_call,
|
||||
:report_file, :report_opt, :report_package, :report_text,
|
||||
:test_call, :test_file, :test_opt, :test_package,
|
||||
:test_text, :watch_file])),
|
||||
PkgAutoloads(:JSON3),
|
||||
PkgAutoloads(
|
||||
:LinearAlgebra,
|
||||
exported = Bindings(callables = [
|
||||
:Adjoint, :BLAS, :Bidiagonal, :BunchKaufman, :Cholesky,
|
||||
:CholeskyPivoted, :ColumnNorm, :Diagonal, :Eigen, :Factorization,
|
||||
:GeneralizedEigen, :GeneralizedSVD, :GeneralizedSchur, :Hermitian,
|
||||
:Hessenberg, :I, :LAPACK, :LAPACKException, :LDLt, :LQ, :LU,
|
||||
:LinearAlgebra, :LowerTriangular, :NoPivot, :PosDefException, :QR,
|
||||
:QRPivoted, :RankDeficientException, :RowMaximum, :RowNonZero, :SVD,
|
||||
:Schur, :SingularException, :SymTridiagonal, :Symmetric, :Transpose,
|
||||
:Tridiagonal, :UniformScaling, :UnitLowerTriangular,
|
||||
:UnitUpperTriangular, :UpperHessenberg, :UpperTriangular,
|
||||
:ZeroPivotException, :adjoint!, :axpby!, :axpy!, :bunchkaufman,
|
||||
:bunchkaufman!, :cholesky, :cholesky!, :cond, :condskeel,
|
||||
:copy_transpose!, :copyto!, :cross, :det, :diag, :diagind, :diagm,
|
||||
:dot, :eigen, :eigen!, :eigmax, :eigmin, :eigvals, :eigvals!,
|
||||
:eigvecs, :factorize, :givens, :hessenberg, :hessenberg!, :isdiag,
|
||||
:ishermitian, :isposdef, :isposdef!, :issuccess, :issymmetric,
|
||||
:istril, :istriu, :kron, :kron!, :ldiv!, :ldlt, :ldlt!, :lmul!,
|
||||
:logabsdet, :logdet, :lowrankdowndate, :lowrankdowndate!,
|
||||
:lowrankupdate, :lowrankupdate!, :lq, :lq!, :lu, :lu!, :lyap, :mul!,
|
||||
:norm, :normalize, :normalize!, :nullspace, :opnorm, :ordschur,
|
||||
:ordschur!, :pinv, :qr, :qr!, :rank, :rdiv!, :reflect!, :rmul!,
|
||||
:rotate!, :schur, :schur!, :svd, :svd!, :svdvals, :svdvals!,
|
||||
:sylvester, :tr, :transpose, :transpose!, :tril, :tril!, :triu,
|
||||
:triu!, :×, :⋅])),
|
||||
PkgAutoloads(:Markdown, exported =
|
||||
Bindings(macros = ["@doc_str", "@md_str"],
|
||||
callables = [:html, :latex])),
|
||||
PkgAutoloads(:Mmap, exported = Bindings(callables = [:mmap])),
|
||||
PkgAutoloads(:Org, exported = Bindings(
|
||||
macros = ["@org_str"],
|
||||
callables = [:Org, :OrgDoc, :org, :parsetree, :tableofcontents, :term])),
|
||||
PkgAutoloads(:Pkg, exported = Bindings(macros = ["@pkg_str"])),
|
||||
PkgAutoloads(:Printf, exported = Bindings(macros = ["@printf"])),
|
||||
PkgAutoloads(:Random, exported = Bindings(callables = [
|
||||
:AbstractRNG, :MersenneTwister, :Random, :RandomDevice, :TaskLocalRNG,
|
||||
:Xoshiro, :bitrand, :rand!, :randcycle, :randcycle!, :randexp,
|
||||
:randexp!, :randn!, :randperm, :randperm!, :randstring, :randsubseq,
|
||||
:randsubseq!, :shuffle, :shuffle!])),
|
||||
PkgAutoloads(:REPL, exported = Bindings(callables =
|
||||
[:AbstractREPL, :BasicREPL, :LineEditREPL, :StreamREPL])),
|
||||
PkgAutoloads(:SHA, exported = Bindings(callables =
|
||||
[:HMAC_CTX, :SHA1_CTX, :SHA224_CTX, :SHA256_CTX, :SHA2_224_CTX,
|
||||
:SHA2_256_CTX, :SHA2_384_CTX, :SHA2_512_CTX, :SHA384_CTX, :SHA3_224_CTX,
|
||||
:SHA3_256_CTX, :SHA3_384_CTX, :SHA3_512_CTX, :SHA512_CTX, :digest!,
|
||||
:hmac_sha1, :hmac_sha224, :hmac_sha256, :hmac_sha2_224, :hmac_sha2_256,
|
||||
:hmac_sha2_384, :hmac_sha2_512, :hmac_sha384, :hmac_sha3_224,
|
||||
:hmac_sha3_256, :hmac_sha3_384, :hmac_sha3_512, :hmac_sha512, :sha1,
|
||||
:sha224, :sha256, :sha2_224, :sha2_256, :sha2_384, :sha2_512, :sha384,
|
||||
:sha3_224, :sha3_256, :sha3_384, :sha3_512, :sha512, :update!])),
|
||||
PkgAutoloads(:Serialization, exported = Bindings(callables = [
|
||||
:AbstractSerializer, :Serializer, :deserialize, :serialize])),
|
||||
PkgAutoloads(
|
||||
:Statistics,
|
||||
exported = Bindings(callables = [
|
||||
:cor, :cov, :mean, :mean!, :median, :median!, :middle, :quantile,
|
||||
:quantile!, :std, :stdm, :var, :varm])),
|
||||
PkgAutoloads(
|
||||
:StatsBase,
|
||||
exported = Bindings(callables = [
|
||||
:AbstractDataTransform, :AbstractHistogram, :AbstractWeights,
|
||||
:AnalyticWeights, :CoefTable, :ConvergenceException,
|
||||
:CovarianceEstimator, :CronbachAlpha, :ECDF, :FrequencyWeights,
|
||||
:Histogram, :L1dist, :L2dist, :Linfdist, :ProbabilityWeights,
|
||||
:SimpleCovariance, :UnitRangeTransform, :UnitWeights, :Weights,
|
||||
:ZScoreTransform, :addcounts!, :autocor, :autocor!, :autocov,
|
||||
:autocov!, :aweights, :competerank, :cor2cov, :corkendall,
|
||||
:corspearman, :counteq, :countmap, :countne, :counts, :cov2cor,
|
||||
:cronbachalpha, :crosscor, :crosscor!, :crosscov, :crosscov!,
|
||||
:crossentropy, :cumulant, :denserank, :ecdf, :entropy, :eweights,
|
||||
:fweights, :genmean, :genvar, :geomean, :gkldiv, :harmmean,
|
||||
:indexmap, :indicatormat, :inverse_rle, :iqr, :kldivergence,
|
||||
:kurtosis, :levelsmap, :mad, :mad!, :maxad, :mean_and_cov,
|
||||
:mean_and_std, :mean_and_var, :meanad, :midpoints, :mode,
|
||||
:model_response, :modes, :moment, :msd, :norepeats, :nquantile,
|
||||
:ordinalrank, :pacf, :pacf!, :partialcor, :percentile,
|
||||
:percentilerank, :proportionmap, :proportions, :psnr, :pweights,
|
||||
:quantilerank, :renyientropy, :rle, :rmsd, :sample, :sample!,
|
||||
:samplepair, :scattermat, :scattermat_zm, :scattermatm, :sem,
|
||||
:skewness, :span, :sqL2dist, :standardize, :summarystats, :tiedrank,
|
||||
:totalvar, :trim, :trim!, :trimvar, :uweights, :variation, :winsor,
|
||||
:winsor!, :wmedian, :wquantile, :wsample, :wsample!, :wsum, :wsum!,
|
||||
:zscore, :zscore!])),
|
||||
PkgAutoloads(:TOML),
|
||||
PkgAutoloads(:Tar),
|
||||
PkgAutoloads(:Test, exported =
|
||||
Bindings(macros = ["@inferred", "@test", "@test_broken",
|
||||
"@test_deprecated", "@test_logs", "@test_nowarn",
|
||||
"@test_skip", "@test_throws", "@test_warn",
|
||||
"@testset"],
|
||||
callables = [:GenericArray, :GenericDict, :GenericOrder,
|
||||
:GenericSet, :GenericString, :LogRecord,
|
||||
:TestLogger, :TestSetException,
|
||||
:detect_ambiguities, :detect_unbound_args])),
|
||||
PkgAutoloads(:UUIDs, exported = Bindings(callables = [
|
||||
:uuid1, :uuid4, :uuid5, :uuid_version])),
|
||||
PkgAutoloads(:WhyNotEqual, exported = Bindings(callables = [:whynot])),
|
||||
])
|
|
@ -0,0 +1,270 @@
|
|||
module Autoloads
|
||||
|
||||
using TOML
|
||||
using REPL
|
||||
|
||||
export Bindings, AutoDot, PkgAutoloads, add_autoloads!
|
||||
|
||||
function __init__()
|
||||
if isinteractive()
|
||||
read_named_envs!()
|
||||
repl_apply_autoloads!()
|
||||
end
|
||||
end
|
||||
|
||||
struct Bindings
|
||||
macros::Vector{Symbol}
|
||||
callables::Vector{Symbol}
|
||||
variables::Vector{Symbol}
|
||||
end
|
||||
|
||||
struct AutoDot end
|
||||
|
||||
Bindings(; macros::Vector{<:Union{Symbol, String}}=Symbol[],
|
||||
callables::Vector{Symbol}=Symbol[], variables=Symbol[]) =
|
||||
Bindings(Symbol.(macros), callables, variables)
|
||||
|
||||
struct PkgAutoloads
|
||||
pkg::Union{Symbol, Expr}
|
||||
exported::Bindings
|
||||
unexported::Union{Bindings, AutoDot}
|
||||
partial::Bool
|
||||
end
|
||||
|
||||
PkgAutoloads(pkg::Union{Symbol, Expr};
|
||||
exported::Bindings = Bindings(Symbol[], Symbol[], Symbol[]),
|
||||
unexported::Union{Bindings, AutoDot} = AutoDot(),
|
||||
partial::Bool = false) =
|
||||
PkgAutoloads(pkg, exported, unexported, partial)
|
||||
|
||||
function PkgAutoloads(mod::Module)
|
||||
hasparent(u::Union, m::Module) =
|
||||
hasparent(u.a, m) && hasparent(u.b, m)
|
||||
hasparent(x::Any, m::Module) =
|
||||
parentmodule(x) === m
|
||||
macros = Symbol[]
|
||||
callables = Symbol[]
|
||||
variables = Symbol[]
|
||||
for symb in names(mod)
|
||||
obj = getglobal(mod, symb)
|
||||
if !hasparent(obj, mod) || obj === mod
|
||||
elseif startswith(String(symb), '@')
|
||||
push!(macros, symb)
|
||||
elseif obj isa Function || obj isa Type
|
||||
push!(callables, symb)
|
||||
else
|
||||
push!(variables, symb)
|
||||
end
|
||||
end
|
||||
PkgAutoloads(nameof(mod),
|
||||
exported = Bindings(
|
||||
macros, callables, variables))
|
||||
end
|
||||
|
||||
const AUTOLOADS =
|
||||
(sources = Dict{Symbol, PkgAutoloads}(),
|
||||
pkgs = Dict{Symbol, Expr}(),
|
||||
exported = (
|
||||
macros = Dict{Symbol, Symbol}(),
|
||||
callables = Dict{Symbol, Symbol}(),
|
||||
variables = Dict{Symbol, Symbol}()),
|
||||
bindings = (
|
||||
macros = Dict{Symbol, Set{Symbol}}(),
|
||||
callables = Dict{Symbol, Set{Symbol}}(),
|
||||
variables = Dict{Symbol, Set{Symbol}}()),
|
||||
ondotaccess = Set{Symbol}(),
|
||||
loaded = Set{Symbol}(),
|
||||
partloaded = Dict{Symbol, Set{Symbol}}())
|
||||
|
||||
function add_autoloads!(autos::PkgAutoloads)
|
||||
!haskey(AUTOLOADS.sources, autos.pkg) ||
|
||||
error("Autoloads for $(autos.pkg) have already been registered")
|
||||
pkgsym, pkg = if autos.pkg isa Symbol
|
||||
autos.pkg, Expr(:., autos.pkg)
|
||||
elseif autos.pkg isa Expr &&
|
||||
Meta.isexpr(autos.pkg, :(.)) &&
|
||||
length(autos.pkg.args) == 2 &&
|
||||
autos.pkg.args[1] isa Symbol &&
|
||||
autos.pkg.args[2] isa QuoteNode &&
|
||||
autos.pkg.args[2].value isa Symbol
|
||||
autos.pkg.args[2].value,
|
||||
Expr(:., autos.pkg.args[1], autos.pkg.args[2].value)
|
||||
end
|
||||
AUTOLOADS.sources[pkgsym] = autos
|
||||
AUTOLOADS.pkgs[pkgsym] = pkg
|
||||
autos.partial && (AUTOLOADS.partloaded[pkgsym] = Set{Symbol}())
|
||||
AUTOLOADS.exported.variables[pkgsym] = pkgsym
|
||||
for category in (:macros, :callables, :variables)
|
||||
autodot = autos.unexported isa AutoDot
|
||||
bindings = autodot || Set(getfield(autos.unexported, category))
|
||||
slots = getfield(AUTOLOADS.exported, category)
|
||||
for binding in getfield(autos.exported, category)
|
||||
if haskey(slots, binding)
|
||||
@warn "An autload for $(String(category)[1:end-1]) $binding already exists"
|
||||
else
|
||||
slots[binding] = pkgsym
|
||||
end
|
||||
autodot || push!(bindings, binding)
|
||||
end
|
||||
if !autodot && !isempty(bindings)
|
||||
getfield(AUTOLOADS.bindings, category)[pkgsym] = bindings
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
const NAMED_ENVS = Vector{Pair{String, Vector{Symbol}}}()
|
||||
|
||||
function read_named_envs!()
|
||||
empty!(NAMED_ENVS)
|
||||
for depot in Base.DEPOT_PATH
|
||||
envdir = joinpath(depot, "environments")
|
||||
isdir(envdir) || continue
|
||||
for env in readdir(envdir)
|
||||
if !isnothing(match(r"^__", env))
|
||||
elseif !isnothing(match(r"^v\d+\.\d+$", env))
|
||||
elseif isfile(joinpath(envdir, env, "Project.toml"))
|
||||
proj = open(TOML.parse, joinpath(envdir, env, "Project.toml"))
|
||||
pkgs = get(proj, "deps", Dict{String, Any}()) |>
|
||||
keys |> collect |> sort .|> Symbol
|
||||
push!(NAMED_ENVS, '@' * env => pkgs)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function find_named_env(pkg::Symbol)
|
||||
for (env, pkgs) in NAMED_ENVS
|
||||
if pkg in pkgs
|
||||
return env
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function autoload_loadpkg(name::Symbol, thing::Union{Symbol, Nothing}=nothing)
|
||||
if name ∉ AUTOLOADS.loaded && haskey(AUTOLOADS.pkgs, name)
|
||||
AUTOLOADS.sources[name].partial && thing in AUTOLOADS.partloaded[name] &&
|
||||
return
|
||||
pkg = AUTOLOADS.pkgs[name]
|
||||
if length(pkg.args) == 1 && isnothing(Base.find_package(String(first(pkg.args))))
|
||||
named_env = find_named_env(name)
|
||||
if !isnothing(named_env)
|
||||
autoload_loadpkg_namedenv(name, named_env, thing)
|
||||
else
|
||||
@warn "Cannot autoload $name, as it is not availible in the current environment stack"
|
||||
end
|
||||
return
|
||||
end
|
||||
try
|
||||
if !isnothing(thing) && AUTOLOADS.sources[name].partial
|
||||
Core.eval(Main, Expr(:import, Expr(:., pkg.args..., thing)))
|
||||
push!(AUTOLOADS.partloaded[name], thing)
|
||||
else
|
||||
Core.eval(Main, Expr(:using, pkg))
|
||||
push!(AUTOLOADS.loaded, name)
|
||||
end
|
||||
nothing
|
||||
catch err
|
||||
@warn "Failed to automatically load $name" err
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function autoload_loadpkg_namedenv(name::Symbol, env::String, thing::Union{Symbol, Nothing}=nothing)
|
||||
pkg = AUTOLOADS.pkgs[name]
|
||||
if Base.active_project() == Base.load_path_expand("@v#.#")
|
||||
pushfirst!(LOAD_PATH, env)
|
||||
try
|
||||
if !isnothing(thing) && AUTOLOADS.sources[name].partial
|
||||
Core.eval(Main, Expr(:import, Expr(:., pkg.args..., thing)))
|
||||
push!(AUTOLOADS.partloaded[name], thing)
|
||||
else
|
||||
Core.eval(Main, Expr(:using, pkg))
|
||||
push!(AUTOLOADS.loaded, name)
|
||||
end
|
||||
finally
|
||||
popfirst!(LOAD_PATH)
|
||||
end
|
||||
else
|
||||
@warn "Cannot autoload $name, but it is availible in the $env environment"
|
||||
end
|
||||
end
|
||||
|
||||
function autoload_scan!(ast::Expr, localbindings::Vector{Symbol}=Symbol[])
|
||||
if Meta.isexpr(ast, :macrocall)
|
||||
pkg = if first(ast.args) isa Symbol && first(ast.args) ∉ localbindings
|
||||
get(AUTOLOADS.exported.macros, first(ast.args), nothing)
|
||||
elseif Meta.isexpr(first(ast.args), :(.)) &&
|
||||
length(first(ast.args).args) == 2 &&
|
||||
(root = first(ast.args).args[1]) isa Symbol &&
|
||||
first(ast.args).args[2] isa QuoteNode &&
|
||||
(child = first(ast.args).args[2].value) isa Symbol &&
|
||||
haskey(AUTOLOADS.bindings.macros, root) &&
|
||||
haskey(AUTOLOADS.bindings.macros[root], child)
|
||||
AUTOLOADS.bindings.macros[root]
|
||||
end
|
||||
if !isnothing(pkg) autoload_loadpkg(pkg, first(ast.args)) end
|
||||
elseif Meta.isexpr(ast, :call)
|
||||
pkg = if first(ast.args) isa Symbol && first(ast.args) ∉ localbindings
|
||||
get(AUTOLOADS.exported.callables, first(ast.args), nothing)
|
||||
elseif Meta.isexpr(first(ast.args), :(.)) &&
|
||||
(root = first(ast.args).args[1]) isa Symbol &&
|
||||
root ∈ AUTOLOADS.ondotaccess
|
||||
root
|
||||
elseif Meta.isexpr(first(ast.args), :(.)) &&
|
||||
length(first(ast.args).args) == 2 &&
|
||||
(root = first(ast.args).args[1]) isa Symbol &&
|
||||
first(ast.args).args[2] isa QuoteNode &&
|
||||
(child = first(ast.args).args[2].value) isa Symbol &&
|
||||
haskey(AUTOLOADS.bindings.callables, root) &&
|
||||
child ∈ AUTOLOADS.bindings.callables[root]
|
||||
root
|
||||
end
|
||||
isnothing(pkg) || autoload_loadpkg(pkg, first(ast.args))
|
||||
elseif Meta.isexpr(ast, :(.)) &&
|
||||
length(ast.args) == 2 &&
|
||||
(root = ast.args[1]) isa Symbol &&
|
||||
ast.args[2] isa QuoteNode &&
|
||||
(child = ast.args[2].value) isa Symbol &&
|
||||
(root ∈ AUTOLOADS.ondotaccess ||
|
||||
(haskey(AUTOLOADS.bindings.variables, root) &&
|
||||
child ∈ AUTOLOADS.bindings.variables[root]) ||
|
||||
(haskey(AUTOLOADS.bindings.callables, root) &&
|
||||
child ∈ AUTOLOADS.bindings.callables[root]))
|
||||
autoload_loadpkg(root)
|
||||
elseif ast.head ∈ (:using, :import, :quote)
|
||||
return
|
||||
elseif Meta.isexpr(ast, :(=)) && first(ast.args) isa Symbol
|
||||
push!(localbindings, first(ast.args))
|
||||
end
|
||||
for arg in ast.args
|
||||
autoload_scan!(arg, localbindings)
|
||||
end
|
||||
end
|
||||
|
||||
function autoload_scan!(var::Symbol, localbindings::Vector{Symbol}=Symbol[])
|
||||
if var ∉ localbindings && var ∉ names(Main)
|
||||
pkg = @something(get(AUTOLOADS.exported.variables, var, nothing),
|
||||
get(AUTOLOADS.exported.callables, var, nothing),
|
||||
Some(nothing))
|
||||
if !isnothing(pkg) autoload_loadpkg(pkg, var) end
|
||||
end
|
||||
end
|
||||
|
||||
autoload_scan!(::Any, ::Vector{Symbol}=Symbol[]) = nothing
|
||||
|
||||
function repl_scan_autoloads!(ast::Expr)
|
||||
@static if VERSION < v"1.9"
|
||||
autoload_scan!(ast)
|
||||
else
|
||||
if Base.active_module() == Main
|
||||
autoload_scan!(ast)
|
||||
end
|
||||
end
|
||||
ast
|
||||
end
|
||||
repl_scan_autoloads!(val::Any) = val
|
||||
|
||||
repl_apply_autoloads!() =
|
||||
pushfirst!(REPL.repl_ast_transforms, repl_scan_autoloads!)
|
||||
|
||||
end
|
|
@ -0,0 +1,71 @@
|
|||
import Base.!, Base.~
|
||||
|
||||
"""
|
||||
~(cmd::AbstractCmd)
|
||||
|
||||
Run `cmd` and return the output as a string.
|
||||
|
||||
```julia-repl
|
||||
julia> ~ `ls /home`
|
||||
"doe"
|
||||
```
|
||||
"""
|
||||
function ~(c::Base.AbstractCmd)
|
||||
strip(read(c, String), '\n')
|
||||
end
|
||||
|
||||
"""
|
||||
!(cmd::AbstractCmd)
|
||||
|
||||
Run `cmd` and print the output to stdout.
|
||||
|
||||
```julia-repl
|
||||
julia> ! `ls /home`
|
||||
doe
|
||||
```
|
||||
|
||||
This can also be combined with `~ cmd` to print and return
|
||||
the output by using `!~ cmd`.
|
||||
```julia-repl
|
||||
julia> !~ `ls /home`
|
||||
doe
|
||||
"doe"
|
||||
```
|
||||
"""
|
||||
function !(c::Base.AbstractCmd)
|
||||
println(~c)
|
||||
end
|
||||
function !(o::SubString{String})
|
||||
println(o)
|
||||
o
|
||||
end
|
||||
|
||||
struct PipeCmds <: Base.AbstractCmd
|
||||
cmds::Vector{<:Base.AbstractCmd}
|
||||
PipeCmds(left::Base.AbstractCmd, right::Base.AbstractCmd) = new([left, right])
|
||||
PipeCmds(left::PipeCmds, right::Base.AbstractCmd) = new([left.cmds; right])
|
||||
PipeCmds(left::Base.AbstractCmd, right::PipeCmds) = new([left; right.cmds])
|
||||
end
|
||||
|
||||
import Base.|
|
||||
|
||||
"""
|
||||
cmd1::AbstractCmd | cmd2::AbstractCmd
|
||||
|
||||
Create a pipleine from `cmd1` to `cmd2`.
|
||||
|
||||
```julia-repl
|
||||
julia> `cat .zshenv` | `wc -l`
|
||||
PipeCmds(Cmd[`cat .zshenv`, `wc -l`])
|
||||
|
||||
When `PipeCmds` are passed to `!` or `~`, the commands contained are
|
||||
expanded inside a `pipeline` call.
|
||||
```
|
||||
"""
|
||||
function (|)(left::Base.AbstractCmd, right::Base.AbstractCmd)
|
||||
PipeCmds(left, right)
|
||||
end
|
||||
|
||||
function ~(c::PipeCmds)
|
||||
strip(read(pipeline(c.cmds...), String), '\n')
|
||||
end
|
|
@ -0,0 +1,79 @@
|
|||
const SETUP_GIT_URL = "https://git.tecosaur.net/tec/Startup.jl"
|
||||
|
||||
const STARTUP_MANAGED_MESSAGE =
|
||||
"# !! Managed by the Setup package !!"
|
||||
|
||||
const STARTUP_CONTENT = """
|
||||
$STARTUP_MANAGED_MESSAGE
|
||||
if VERSION < v"1.7"
|
||||
@warn "Setup disabled as Julia \$VERSION < 1.7 is unsupported"
|
||||
else
|
||||
let pkg_id = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg")
|
||||
Pkg = Base.loaded_modules[pkg_id]
|
||||
isnothing(Base.find_package("Setup")) &&
|
||||
Pkg.develop(path=joinpath(@__DIR__, "Setup"))
|
||||
end
|
||||
|
||||
using Setup
|
||||
|
||||
Setup.ensurepkg("Revise")
|
||||
@async @eval using Revise
|
||||
end
|
||||
"""
|
||||
|
||||
function _install_paths()
|
||||
config = joinpath(first(Base.DEPOT_PATH), "config")
|
||||
startup = joinpath(config, "startup.jl")
|
||||
setup = joinpath(config, "Setup")
|
||||
(; config, startup, setup)
|
||||
end
|
||||
|
||||
function install()
|
||||
(; config, startup, setup) = _install_paths()
|
||||
isdir(config) || mkpath(config)
|
||||
if isfile(startup)
|
||||
if readline(startup) == STARTUP_MANAGED_MESSAGE
|
||||
isdir(setup) && update()
|
||||
else
|
||||
@info "Moving original startup file to $(startup).old"
|
||||
mv(startup, startup * ".old")
|
||||
end
|
||||
end
|
||||
write(startup, STARTUP_CONTENT)
|
||||
if isdir(setup) && !isdir(joinpath(setup, ".git"))
|
||||
@warn "Setup folder exists, but it is not a git repository. Re-creating..."
|
||||
rm(setup, recursive=true)
|
||||
end
|
||||
if !isdir(setup)
|
||||
success(`git clone $SETUP_GIT_URL $setup`) ||
|
||||
@error "Failed to clone $SETUP_GIT_URL"
|
||||
end
|
||||
end
|
||||
|
||||
function uninstall()
|
||||
(; startup, setup) = _install_paths()
|
||||
isfile(startup) && readline(startup) != STARTUP_MANAGED_MESSAGE &&
|
||||
rm(startup)
|
||||
isfile(startup * ".old") && !isfile(startup) && mv(startup * ".old", startup)
|
||||
if isdir(joinpath(setup, ".git")) && !isempty(read(Cmd(`git status --porcelain=v1`, dir=setup)))
|
||||
@warn "Unstaged changes in $(setup)! Manually fix this and try again."
|
||||
elseif isdir(setup)
|
||||
rm(setup, recursive=true, force=true)
|
||||
end
|
||||
end
|
||||
|
||||
function update()
|
||||
(; startup, setup) = _install_paths()
|
||||
if readline(startup) != STARTUP_MANAGED_MESSAGE
|
||||
@warn "Startup.jl is not managed by Setup, re-installing"
|
||||
return install()
|
||||
else
|
||||
write(startup, STARTUP_CONTENT)
|
||||
end
|
||||
if !ispath(setup) || !isdir(joinpath(setup, ".git"))
|
||||
ispath(setup) || @warn "Setup missing, re-installing"
|
||||
return install()
|
||||
end
|
||||
success(Cmd(`git pull`, dir=setup)) ||
|
||||
@error "Failed to update Setup"
|
||||
end
|
|
@ -0,0 +1,133 @@
|
|||
module PkgStack
|
||||
|
||||
import Pkg
|
||||
import Markdown: @md_str
|
||||
|
||||
function stack(envs)
|
||||
if isempty(envs)
|
||||
printstyled(" The current stack:\n", bold=true)
|
||||
println.(" " .* LOAD_PATH)
|
||||
else
|
||||
for env in envs
|
||||
if env ∉ LOAD_PATH
|
||||
push!(LOAD_PATH, env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
const STACK_SPEC = Pkg.REPLMode.CommandSpec(
|
||||
name = "stack",
|
||||
api = stack,
|
||||
help = md"""
|
||||
stack envs...
|
||||
|
||||
Stack another environment.
|
||||
""",
|
||||
description = "Stack another environment",
|
||||
completions = Pkg.REPLMode.complete_activate,
|
||||
should_splat = false,
|
||||
arg_count = 0 => Inf)
|
||||
|
||||
function unstack(envs)
|
||||
if isempty(envs)
|
||||
printstyled(" The current stack:\n", bold=true)
|
||||
println.(" " .* LOAD_PATH)
|
||||
else
|
||||
deleteat!(LOAD_PATH, sort(filter(!isnothing, indexin(envs, LOAD_PATH))))
|
||||
end
|
||||
end
|
||||
|
||||
const UNSTACK_SPEC = Pkg.REPLMode.CommandSpec(
|
||||
name = "unstack",
|
||||
api = unstack,
|
||||
help = md"""
|
||||
unstack envs...
|
||||
|
||||
Unstack a previously stacked environment.
|
||||
""",
|
||||
description = "Unstack an environment",
|
||||
completions = (_, partial, _, _) ->
|
||||
filter(p -> startswith(p, partial), LOAD_PATH),
|
||||
should_splat = false,
|
||||
arg_count = 0 => Inf)
|
||||
|
||||
function environments()
|
||||
envs = String[]
|
||||
for depot in Base.DEPOT_PATH
|
||||
envdir = joinpath(depot, "environments")
|
||||
isdir(envdir) || continue
|
||||
for env in readdir(envdir)
|
||||
if !isnothing(match(r"^__", env))
|
||||
elseif !isnothing(match(r"^v\d+\.\d+$", env))
|
||||
else
|
||||
push!(envs, '@' * env)
|
||||
end
|
||||
end
|
||||
end
|
||||
envs = Base.DEFAULT_LOAD_PATH ∪ LOAD_PATH ∪ envs
|
||||
for env in envs
|
||||
if env in LOAD_PATH
|
||||
print(" ", env)
|
||||
else
|
||||
printstyled(" ", env, color=:light_black)
|
||||
if env in Base.DEFAULT_LOAD_PATH
|
||||
printstyled(" (unloaded)", color=:light_red)
|
||||
end
|
||||
end
|
||||
if env == "@"
|
||||
printstyled(" [current environment]", color=:light_black)
|
||||
elseif env == "@v#.#"
|
||||
printstyled(" [global environment]", color=:light_black)
|
||||
elseif env == "@stdlib"
|
||||
printstyled(" [standard library]", color=:light_black)
|
||||
elseif env in LOAD_PATH
|
||||
printstyled(" (loaded)", color=:green)
|
||||
end
|
||||
print('\n')
|
||||
end
|
||||
end
|
||||
|
||||
const ENVS_SPEC = Pkg.REPLMode.CommandSpec(
|
||||
name = "environments",
|
||||
short_name = "envs",
|
||||
api = environments,
|
||||
help = md"""
|
||||
environments|envs
|
||||
|
||||
List all known named environments.
|
||||
""",
|
||||
description = "List all known named environments",
|
||||
arg_count = 0 => 0)
|
||||
|
||||
const SPECS = Dict(
|
||||
"stack" => STACK_SPEC,
|
||||
"unstack" => UNSTACK_SPEC,
|
||||
"environments" => ENVS_SPEC,
|
||||
"envs" => ENVS_SPEC)
|
||||
|
||||
function __init__()
|
||||
# add the commands to the repl
|
||||
activate = Pkg.REPLMode.SPECS["package"]["activate"]
|
||||
let temp = Pkg.REPLMode.OptionSpec("temp", "t", :temp => true, false)
|
||||
activate.option_specs["temp"] = temp
|
||||
activate.option_specs["t"] = temp
|
||||
end
|
||||
activate_modified = Pkg.REPLMode.CommandSpec(
|
||||
activate.canonical_name,
|
||||
"a", # Modified entry, short name
|
||||
activate.api,
|
||||
activate.should_splat,
|
||||
activate.argument_spec,
|
||||
activate.option_specs,
|
||||
activate.completions,
|
||||
activate.description,
|
||||
activate.help)
|
||||
SPECS["activate"] = activate_modified
|
||||
SPECS["a"] = activate_modified
|
||||
Pkg.REPLMode.SPECS["package"] = merge(Pkg.REPLMode.SPECS["package"], SPECS)
|
||||
# update the help with the new commands
|
||||
copy!(Pkg.REPLMode.help.content, Pkg.REPLMode.gen_help().content)
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,153 @@
|
|||
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()
|
||||
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))
|
||||
else
|
||||
@info "Consider installing $(pkg.name) for better in-terminal image previews"
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue