By: Jacob Zelko
Re-posted from: https://jacobzelko.com/08312022162228-julia-lsp-setup/index.html
Setting Up Julia LSP for Neovim
Date: August 31 2022
Summary: An explanation of how to setup the Julia LSP for Neovim
Keywords: ##summary #neovim #julia #programming #archive
Bibliography
Not Available
Table of Contents
-
- General Guide
- References
- References
- Discussion:
General Guide
This is from Fredrik Ekre at the Julia Discourse with some minimal changes and notes from me:
LanguageServer is somewhat slow to start so it is very useful to use a custom sysimage using PackageCompiler to reduce this time. On my machine I get the first response after 20+ seconds, but with a custom sysimage I can execute LS commands instantaneously.
Here is my setup:
-
Install
Mason.nvim
ornvim-lspconfig
and installjulials
(it may also be called something like Julia Language Server Protocol). -
Modify
init.vim
orinit.lua
to use a custom Julia executable (if it exists):
require'lspconfig'.julials.setup{
on_new_config = function(new_config, _)
local julia = vim.fn.expand("~/.julia/environments/nvim-lspconfig/bin/julia")
if require'lspconfig'.util.path.is_file(julia) then
vim.notify("Hello!")
new_config.cmd[1] = julia
end
end
}
(OPTIONAL) If you use Packer to manage your vim setup, run PackerCompile
.
NOTE: If you notice, there is a small line named vim.notify("Hello!")
. This is to test that julials
is engaged when accessing a Julia file – you can check that it is engaged by writing :messages
in vim. You should see "Hello!" appear. This line can then safely be removed.
-
Create the
nvim-lspconfig
Julia environment by running the following in your shell:
julia --project=~/.julia/environments/nvim-lspconfig -e 'using Pkg; Pkg.add("LanguageServer")'
And then navigate to the directory at ~.julia/environment/nvim-lspconfig
.
-
Copy the following makefile (courtesy of Fredrik Ekre) the
nvim-lspconfig
directory with the namemakefile
:
# MIT License. Copyright (c) 2021 Fredrik Ekre
#
# This Makefile can be used to build a custom Julia system image for LanguageServer.jl to
# use with neovims built in LSP support. An up-to date version of this Makefile can be found
# at https://github.com/fredrikekre/.dotfiles/blob/master/.julia/environments/nvim-lspconfig/Makefile
#
# Usage instructions:
#
# 1. Update the neovim configuration to use a custom julia executable. If you use
# nvim-lspconfig (recommended) you can modify the setup call to the following:
#
# require("lspconfig").julials.setup({
# on_new_config = function(new_config, _)
# local julia = vim.fn.expand("~/.julia/environments/nvim-lspconfig/bin/julia")
# if require("lspconfig").util.path.is_file(julia) then
# new_config.cmd[1] = julia
# end
# end,
# -- ...
# })
#
# 2. Place this Makefile in ~/.julia/environments/nvim-lspconfig (create the directory if
# it doesn't already exist).
#
# 3. Change directory to ~/.julia/environments/nvim-lspconfig and run `make`. This will
# start up neovim in a custom project with a julia process that recods compiler
# statements. Follow the instructions in the opened source file, and then exit neovim.
#
# 4. Upon exiting neovim PackageCompiler.jl will compile a custom system image which will
# automatically be used whenever you work on Julia projects in neovim.
#
# Update instructions:
#
# To update the system image (e.g. when upgrading Julia or upgrading LanguageServer.jl or
# it's dependencies) run the following commands from the
# ~/.julia/environments/nvim-lspconfig directory:
#
# julia --project=. -e 'using Pkg; Pkg.update()'
# makeJULIA=$(shell which julia)
JULIA_PROJECT=
SRCDIR:=$(shell dirname $(abspath $(firstword $(MAKEFILE_LIST))))
ifeq ($(shell uname -s),Linux)
SYSIMAGE=languageserver.so
else
SYSIMAGE=languageserver.dylib
endifdefault: $(SYSIMAGE)$(SYSIMAGE): Manifest.toml packagecompiler/Manifest.toml packagecompiler/precompile_statements.jl
JULIA_LOAD_PATH=${PWD}:${PWD}/packagecompiler:@stdlib ${JULIA} -e 'using PackageCompiler; PackageCompiler.create_sysimage(:LanguageServer, sysimage_path="$(SYSIMAGE)", precompile_statements_file="packagecompiler/precompile_statements.jl")'Manifest.toml: Project.toml
JULIA_LOAD_PATH=${PWD}/Project.toml:@stdlib ${JULIA} -e 'using Pkg; Pkg.instantiate()'Project.toml:
JULIA_LOAD_PATH=${PWD}/Project.toml:@stdlib ${JULIA} -e 'using Pkg; Pkg.add("LanguageServer")'packagecompiler/Manifest.toml: packagecompiler/Project.toml
JULIA_LOAD_PATH=${PWD}/packagecompiler/Project.toml:@stdlib ${JULIA} -e 'using Pkg; Pkg.instantiate()'packagecompiler/Project.toml:
mkdir -p packagecompiler
JULIA_LOAD_PATH=${PWD}/packagecompiler/Project.toml:@stdlib ${JULIA} -e 'using Pkg; Pkg.add("PackageCompiler")'packagecompiler/precompile_statements.jl: Manifest.toml bin/julia
TMPDIR=$(shell mktemp -d) && \
cd $${TMPDIR} && \
JULIA_LOAD_PATH=: ${JULIA} -e 'using Pkg; Pkg.generate("Example")' 2> /dev/null && \
cd Example && \
JULIA_LOAD_PATH=$${PWD}:@stdlib ${JULIA} -e 'using Pkg; Pkg.add(["JSON", "fzf_jll", "Random", "Zlib_jll"])' 2> /dev/null && \
JULIA_LOAD_PATH=$${PWD}:@stdlib ${JULIA} -e 'using Pkg; Pkg.precompile()' 2> /dev/null && \
echo "$$PACKAGE_CONTENT" > src/Example.jl && \
JULIA_TRACE_COMPILE=1 nvim src/Example.jl && \ # NOTE: You may need to check that neovim is correctly on your path
rm -rf $${TMPDIR}bin/julia:
mkdir -p bin
echo "$$JULIA_SHIM" > $@
chmod +x $@clean:
rm -rf $(SYSIMAGE) packagecompiler bin.PHONY: clean defaultexport JULIA_SHIM
define JULIA_SHIM
#!/bin/bash
JULIA=${JULIA}
if [[ $${JULIA_TRACE_COMPILE} = "1" ]]; then
exec $${JULIA} --trace-compile=${PWD}/packagecompiler/precompile_statements.jl "$$@"
elif [[ -f ${PWD}/$(SYSIMAGE) ]]; then
exec $${JULIA} --sysimage=${PWD}/$(SYSIMAGE) "$$@"
else
exec $${JULIA} "$$@"
fi
endefexport PACKAGE_CONTENT
define PACKAGE_CONTENT
# This file is opened in neovim with a LanguageServer.jl process that records Julia
# compilation statements for creating a custom sysimage.
#
# This file has a bunch of linter errors which will exercise the linter and record
# statements for that. When the diagnostic messages corresponding to those errors show up in
# the buffer the language server should be ready to accept other commands (note: this may
# take a while -- be patient). Here are some suggestions for various LSP functionality that
# can be exercised (your regular keybindings should work):
#
# - :lua vim.lsp.buf.hover()
# - :lua vim.lsp.buf.definition()
# - :lua vim.lsp.buf.references()
# - :lua vim.lsp.buf.rename()
# - :lua vim.lsp.buf.formatting()
# - :lua vim.lsp.buf.formatting_sync()
# - :lua vim.lsp.buf.code_action()
# - Tab completion (if you have set this up using LSP)
# - ...
#
# When you are finished, simply exit neovim and PackageCompiler.jl will use all the recorded
# statements to create a custom sysimage. This sysimage will be used for the language server
# process in the future, and should result in almost instant response.module Exampleimport JSON
import fzf_jll
using Random
using Zlib_jllfunction hello(who, notused)
println("hello", who)
shuffle([1, 2, 3])
shoffle([1, 2, 3])
fzzf = fzf_jll.fzzf()
fzf = fzf_jll.fzf(1)
JSON.print(stdout, Dict("hello" => [1, 2, 3]), 2, 123)
JSON.print(stdout, Dict("hello" => [1, 2, 3]))
hi(who)
return Zlib_jll.libz
endfunction world(s)
if s == nothing
hello(s)
else
hello(s)
end
x = [1, 2, 3]
for i in 1:length(x)
println(x[i])
end
endend # module
endef
-
Run
make
. This will set up a dummy project and launch nvim with julia recording everything that is compiled. Wait until the LanguageServer responds (there are a bunch of things in this dummy project that will result in warnings) and then run some LanguageServer commands, for example::lua vim.lsp.buf.hover()
to fetch documentation). -
Quit vim.
-
PackageCompiler will now build a custom
languageserver.so
sysimage. -
Enjoy the Julia LSP!