Julia is a language designed for scientific computing. Since it's not a general-purpose language, it's lesser-known in the community of programmers, and thus the support is not as rich as Python. The good news is that the community of Julia is mature enough, the editor support in VS Code is great, and it requires just a little effort to have a pleasant editing experience in Neovim.
Basic setup #
I'm using Neovim with NvChad. NvChad comes with simple plugin management based on packer.nvim, LSP installation and configuring stuff with mason.nvim and nvim-lspconfig, Syntax highlighting with nvim-treesitter.
To enable syntax highlighting for Julia, just run
:TSInstall julia
To install Julia LSP, first, open the mason window and find julia-lsp
, and install it. Then you need to configure the LSP as described in NvChad's documentation
-- ~/.config/nvim/lua/custom/plugins/lspconfig.lua
local on_attach = require("plugins.configs.lspconfig").on_attach
local capabilities = require("plugins.configs.lspconfig").capabilities
local lspconfig = require "lspconfig"
local servers = { "html", "cssls", "clangd", "julials" }
-- a bunch of other LSPs... ^^^^^^^^^
for _, lsp in ipairs(servers) do
lspconfig[lsp].setup {
on_attach = on_attach,
capabilities = capabilities,
}
end
Another cool feature of Julia is that you can use Unicode symbols for variable names and some operations. Typing \alpha<Tab>
in Julia REPL will give you the symbol α
. If used properly, it can make the math-heavy code more readable and elegant. BeautifulAlgorithms.jl has a bunch of examples. And enabling this feature in Neovim is as easy as installing the julia-vim plugin.
-- ~/.config/nvim/lua/custom/plugins/init.lua
return {
["JuliaEditorSupport/julia-vim"] = {},
-- Others...
}
And don't forget to run
:PackerCompile
:PackerInstall
The remaining problem: formatting #
NvChad used to come with null-ls.nvim installed, but removed it afterward. It's easy to install null-ls.nvim.
[!NOTE]
null-ls
is not maintained. Please use its forknone-ls
instead.
Although JuliaFormatter.jl provides formatting for Julia, null-ls doesn't have built-in support for that. Therefore, I must write the integration myself.
In Julia REPL: (]
means entering Pkg
mode)
]add JuliaFormatter
cd ~/.config/nvim/lua/custom
mkdir plugins/null-ls
mv plugins/null-ls.lua plugins/null-ls/init.lua
vim plugins/null-ls/julia.lua
local h = require("null-ls.helpers")
local methods = require("null-ls.methods")
return {
method = methods.internal.FORMATTING,
name = "JuliaFormatter",
meta = {
url = "https://github.com/domluna/JuliaFormatter.jl",
description = "An opinionated code formatter for Julia.",
},
filetypes = { "julia" },
generator = h.formatter_factory {
command = "juliafmt",
args = {
"-e",
"using JuliaFormatter; println(format_text(String(read(stdin))))",
},
to_stdin = true,
timeout = 30000,
}
}
And add julia-formatter
to null-ls sources:
local ok, julia = pcall(require, "custom.plugins.null-ls.julia")
if not ok then
return
end
-- ...
local sources = {
-- ...
julia,
}
null_ls.setup {
sources = sources,
}
Then you just open a Julia file with Neovim, wait for the LSP ready, and hit <leader> f m
to format the code.
However, this is not the final solution. JuliaFormatter can be very slow to format files because the JIT compilation is quite slow. The formatting usually takes 10 to 20 seconds. One solution is provided at JuliaFormatter.jl#633 (issue-comment). We just need to precompile the library. Also, JuliaFormatter doesn't pick up project configuration if passed via stdin, so I have to change the juliafmt
file whenever I want to change some options, and the changes are applied "globally". That's quite inconvenient, so we'd better use a temp file.
My solution is to put the following content to ~/.local/bin/juliafmt
:
#!/bin/bash
OUTPUT_SYSIMAGE=~/.local/lib/juliafmt.so
FORMAT_CMD="using JuliaFormatter; format_file(\"$1\")"
if [ "$1" == "--compile" ]; then
echo "using JuliaFormatter; format_file(\"/tmp/juliafmt.jl\")" > /tmp/juliafmt.jl
julia -e 'using Pkg
Pkg.activate(temp=true)
Pkg.add(["JuliaFormatter", "PackageCompiler"])
using PackageCompiler
create_sysimage(
["JuliaFormatter"],
sysimage_path="'$OUTPUT_SYSIMAGE'",
precompile_execution_file="/tmp/juliafmt.jl"
)'
else
if [ -f "$OUTPUT_SYSIMAGE" ]; then
julia -J $OUTPUT_SYSIMAGE -e "$FORMAT_CMD"
else
julia -e "$FORMAT_CMD"
fi
fi
And run
chmod u+x ~/.local/bin/juliafmt
# If you don't run compile, the script will fall back to old JIT-every-time behavior
juliafmt --compile
If everything works, now you have a juliafmt
executable to replace julia -e 'blah blah'
. Now we can just change the plugins/null-ls/julia.lua
to
local h = require "null-ls.helpers"
local methods = require "null-ls.methods"
return {
method = methods.internal.FORMATTING,
name = "JuliaFormatter",
meta = {
url = "https://github.com/domluna/JuliaFormatter.jl",
description = "An opinionated code formatter for Julia.",
},
filetypes = { "julia" },
generator = h.formatter_factory {
command = "juliafmt",
to_temp_file = true,
from_temp_file = true,
args = {
"$FILENAME",
},
},
}