Has anyone configured ENV[EDITOR"] in their startup.jl to use Visual Studio Code as their editor?
Yes I use ENV["JULIA_EDITOR"] = "code"
and works ok
Other than starting Revise and setting the editor I have a macro for partial application:
module My_partial
export @p
function count_(expr::Expr)
count = 0
for arg in expr.args
count += count_(arg)
end
count
end
function count_(expr)
convert(Int64,expr == :_)::Int64
end
function subst_!(expr::Expr,slist)
for i in eachindex(expr.args)
expr.args[i]= subst_!(expr.args[i],slist)
end
expr
end
function subst_!(sym,slist)
if (sym == :_) & (!isempty(slist))
sym = popfirst!(slist)
else
return esc(sym)
end
end
macro p(expr::Expr)
fname = gensym()
symnames = [gensym() for i in 1:My_partial.count_(expr)]
args = copy(symnames)
My_partial.subst_!(expr,symnames)
(quote $fname($(args...)) = $expr end)
end
end
using Main.My_partial
and the obligatory (for me) definitions to stack array of arrays to a matrix:
vstack(v) = permutedims(reduce(hcat,v))
hstack(v) = hcat(v...)
I set the environment variable for my editor in ~/.bashrc
, and I generally launch Julia from bash, so I don’t use startup.jl
to set environment variables like this.
But I think the name code
is absolutely absurd, and it’s not on my PATH
. I use the symlink vscode
, and so my startup.jl
contains:
# `vscode` is my symlink to the Visual Studio Code executable.
# Tell Julia how to open files with `vscode`, modified from: `InteractiveUtils/src/editless.jl`
using InteractiveUtils # `using` instead of `import` due to https://github.com/JuliaLang/julia/issues/40192
InteractiveUtils.define_editor(["vscode"]) do cmd, path, line
`$cmd -g $path:$line`
end
Along the same lines of making things prettier, I keep a pretty_rational
function in my startup to change the default printing of Rational
numbers when I want to. I’m not brave enough to make it my default setting, though.
function pretty_rational()
@eval Base.show(io::IO, x::Rational) =
x.den==1 ? print(io, x.num) : print(io, "$(x.num)/$(x.den)")
end
Now this
julia> a = [x//y for x in 1:10, y in 1:10]
10×10 Matrix{Rational{Int64}}:
1//1 1//2 1//3 1//4 1//5 1//6 1//7 1//8 1//9 1//10
2//1 1//1 2//3 1//2 2//5 1//3 2//7 1//4 2//9 1//5
3//1 3//2 1//1 3//4 3//5 1//2 3//7 3//8 1//3 3//10
4//1 2//1 4//3 1//1 4//5 2//3 4//7 1//2 4//9 2//5
5//1 5//2 5//3 5//4 1//1 5//6 5//7 5//8 5//9 1//2
6//1 3//1 2//1 3//2 6//5 1//1 6//7 3//4 2//3 3//5
7//1 7//2 7//3 7//4 7//5 7//6 1//1 7//8 7//9 7//10
8//1 4//1 8//3 2//1 8//5 4//3 8//7 1//1 8//9 4//5
9//1 9//2 3//1 9//4 9//5 3//2 9//7 9//8 1//1 9//10
10//1 5//1 10//3 5//2 2//1 5//3 10//7 5//4 10//9 1//1
becomes
julia> pretty_rational()
julia> a
10×10 Matrix{Rational{Int64}}:
1 1/2 1/3 1/4 1/5 1/6 1/7 1/8 1/9 1/10
2 1 2/3 1/2 2/5 1/3 2/7 1/4 2/9 1/5
3 3/2 1 3/4 3/5 1/2 3/7 3/8 1/3 3/10
4 2 4/3 1 4/5 2/3 4/7 1/2 4/9 2/5
5 5/2 5/3 5/4 1 5/6 5/7 5/8 5/9 1/2
6 3 2 3/2 6/5 1 6/7 3/4 2/3 3/5
7 7/2 7/3 7/4 7/5 7/6 1 7/8 7/9 7/10
8 4 8/3 2 8/5 4/3 8/7 1 8/9 4/5
9 9/2 3 9/4 9/5 3/2 9/7 9/8 1 9/10
10 5 10/3 5/2 2 5/3 10/7 5/4 10/9 1
On Windows 10, in my case, I need ENV["JULIA_EDITOR"] = "code.cmd -g"
.
Details:
julia> ENV["JULIA_EDITOR"] = "code "
julia> edit("test_4.jl", 7)
ERROR: IOError: could not spawn `code -g test_4.jl:7`: no such file or directory (ENOENT)
julia> ENV["JULIA_EDITOR"] = "code.cmd -g"
"code.cmd -g"
julia> edit("test_4.jl", 7)
julia> split(ENV["PATH"], ';')
12-element Vector{SubString{String}}:
...
"C:\\Users\\f\\AppData\\Local\\Programs\\Microsoft VS Code\\bin"
...
`
I like automatically activating local envs when they exist
if isfile("Project.toml") && isfile("Manifest.toml")
Pkg.activate(".")
end
My startup.jl has become a place for tools that are too simple to be behind an import, and probably too opinionated for Base
"""
@subprocess ex
@subprocess ex wait=false
Execute `ex` in a subprocess that mimics the current process configuration.
Returns the constructed `Process`.
See [`Base.julia_cmd`](@ref) for the subprocess configuration forwarding that is used.
```julia-repl
julia> println("hello from $(Base.getpid())")
hello from 35640
julia> @subprocess println("hello from $(Base.getpid())")
hello from 43056
Process(`/home/user/julia/julia -Cnative -J/home/user/julia/usr/lib/julia/sys.dylib -g1 -e 'println("hello from $(Base.getpid())")'`, ProcessExited(0))
```
"""
macro subprocess(ex, wait=true)
quote
local ex_str = $(esc(sprint(Base.show_unquoted,ex)))
run(`$(Base.julia_cmd()) -e "$(ex_str)"`, wait = $(wait))
end
end
# See: https://github.com/IanButterworth/julia/tree/ib/subprocess_macro
"""
@repeat call
@repeat n call
@repeat expr call
Repeat `call` until interrupt, or `n` times, and discard the output.
If an expression is given that returns an Integer, repeat that many times.
If an expression is given that returns a Bool, repeat until false.
```julia
julia> @repeat println("hello")
hello
hello
^ChelloERROR: InterruptException:
...
julia> @repeat 3 println("hello")
hello
hello
hello
julia> @repeat 2 + 1 println("hello")
hello
hello
hello
julia> @repeat rand() > 0.5 println("hello")
hello
hello
```
"""
macro repeat(exs...)
if length(exs) == 1
quote
while true
$(esc(first(exs)))
end
end
elseif length(exs) == 2
terms = first(exs)
ex = last(exs)
if terms <: Integer
quote
for _ = 1:$(esc(terms))
$(esc(ex))
end
end
elseif terms isa Expr
quote
local terms_eval = $(esc(terms))
if terms_eval isa Bool
if terms_eval
$(esc(ex)) # do once given that terms has been evaled once already
while $(esc(terms))
$(esc(ex))
end
end
elseif terms_eval isa Integer
for _ in 1:terms_eval
$(esc(ex))
end
else
throw(ArgumentError("@repeat first argument must return an Integer or a Bool"))
end
end
else
throw(ArgumentError("@repeat first argument must be an Integer literal, or an expression that returns an Integer or Bool"))
end
else
throw(ArgumentError("Too many arguments passed to @repeat"))
end
end
# See: https://github.com/JuliaLang/julia/pull/41455
@ianshmean, if you have a minute, could you please add a little preamble of explanation for non-experts.
Thank you!
Happy to. Do you mean about the macros themself, beyond the examples in the docstrings?
Or my rationale for not/abandoning PR-ing them to Base?
Basically, kindly give some hints on: why a lambda user would like to add your code to his startup.jl? What does it accomplish?
Sorry if it is so obvious to you.
For @repeat
I’ve found the occasional need to quickly run some functions n times, or until something is false, or timedout, particularly when developing hardware controls.
For instance
julia> @repeat 3 foo()
is a little simpler to write than
julia> [foo() for _ in 1:3];
More examples in https://github.com/JuliaLang/julia/pull/41455
For @subprocess
if I wanted to do something in a clean julia session quickly I added that largely because I never set up julia
symlinks to julia binaries, so never have a simple julia
pointing to the right place.
So
julia> @subprocess println("hello from a new session")
is easier than
julia> run(`$(Base.julia_cmd()) -e "println(\"hello from a new session\")"`
or
shell> /long/path/to/julia -e "println(\"hello from a new session\")"
The first PR wasn’t very popular. The second doesn’t feel like it would be used widely enough for inclusion in Base (?), so I just kept them local
As seen on twitter, a few people have been adding Term.jl to their startup to style logging, errors and repr. Leaving this here in case anyone finds it useful.
try
import Term: install_term_stacktrace, install_term_logger, install_term_repr
install_term_stacktrace()
install_term_logger()
install_term_repr()
catch e
@warn "Error initializing term" exception=(e, catch_backtrace())
end
Enjoy!
So many great little findings in this thread!
I’ve also recently added a package template as per the docs of PkgTemplates.
# Package templates
function template()
@eval begin
using PkgTemplates
Template(;
user="my-username",
dir="~/code",
authors="Me",
julia=v"1.7",
plugins=[
Git(; manifest=true),
GitHubActions(),
Codecov(),
Documenter{GitHubActions}(),
Citation(),
RegisterAction(),
BlueStyleBadge(),
ColPracBadge(),
],
)
end
end
Then just proceed as always:
t = template()
t("MyPkg")
Works like a charm!
Since this thread seems to still be alive, I might as well toss what I’ve currently got going on.
import Pkg as var"#Pkg" # Don't import globally
isnothing(Base.find_package("Setup")) &&
var"#Pkg".develop(path=joinpath(@__DIR__, "Setup"))
using Setup
@async let
ESSENTIAL_PACKAGES =
["Revise", "DataFrames", "CSV", "BenchmarkTools"]
EAGER_PACKAGES =
["DataFrames", "Statistics", "StatsBase", "BenchmarkTools"]
for pkg in ESSENTIAL_PACKAGES
if isnothing(Base.find_package(pkg))
var"#Pkg".add(pkg)
end
end
@eval using Revise
for pkg in EAGER_PACKAGES
if !isnothing(Base.find_package(pkg))
Core.eval(Main, :(using $(Symbol(pkg))))
end
end
Setup.termtitle(pwd())
end
where Setup
is a whole bunch of setup bundled as a module.
.julia/config
├── Setup/
│ ├── ext/
│ │ ├── cairomakie.jl
│ │ ├── gadfly.jl
│ │ ├── hdf5.jl
│ │ ├── ohmyrepl.jl
│ │ └── prettycolors.jl
│ ├── src/
│ │ ├── about.jl
│ │ ├── cmdpuns.jl
│ │ ├── colorfulmethods.jl
│ │ ├── pkgstack.jl
│ │ ├── Setup.jl
│ │ └── termsetup.jl
│ ├── Manifest.toml
│ └── Project.toml
├── sysimages/
│ └── <a collection of build scripts>
├── Faces.toml
└── startup.jl
All in all, it’s ~900 lines of setup.
This increaces the Julia REPL startup time by ~30%, from 70ms to 90ms.
Do you have auto-updating system images in Setup/sysimages ?
Is it possible for you to give access to your Setup package code ? It looks inspiring !
I always use project specific environments. If there does not yet exist one, I create one. If there already exists one, I activate and instantiate it.
using Pkg
working_directory = dirname(@__FILE__)
cd(working_directory)
if Base.active_project() != dirname(@__FILE__) * "/Project.toml"
pkg"activate ."
pkg"instantiate"
end
you might find @__DIR__
useful rather than dirname(@__FILE__)
Also, there’s joinpath
which is platform agnostic.