Neovim + LanguageServer.jl

neovim has built in lsp support and can be integrated with any language server. I thought I’d describe the set up for using LanguageServer.jl and neovim.

You need to use the latest version of neovim at the moment. I’m using the HEAD, the version information is below.

$ nvim --version
NVIM v0.5.0-nightly-71-g1f56f9a4b
Build type: Release
LuaJIT 2.0.5

Use your favorite plugin manager to add the following package neovim/nvim-lsp to your vimrc. I am using vim-plug.

Plug 'neovim/nvim-lsp'  | " collection of common configurations for the Nvim LSP client.

Also add the following lines anywhere in your vimrc below the plugin section.

lua << EOF
    require'nvim_lsp'.julials.setup{}
EOF

And you can configure maps to run vim.lsp.{functions}:

nnoremap <silent> <leader>ld    <cmd>lua vim.lsp.buf.declaration()<CR>
nnoremap <silent> <leader>lh    <cmd>lua vim.lsp.buf.hover()<CR>
nnoremap <silent> <leader>ld    <cmd>lua vim.lsp.util.show_line_diagnostics()<CR>
nnoremap <silent> <leader>lk    <cmd>lua vim.lsp.buf.signature_help()<CR>
nnoremap <silent> <leader>lr    <cmd>lua vim.lsp.buf.references()<CR>

You can run :LspInstall julials in neovim to install LanguageServer.jl. Or you can do so manually in your global environment.

And that’s it!

I’ve posted my configuration here if anyone wants to take a look at the full file for how this is set up:

25 Likes

I was trying to use your solution in neovim, but unfortunately it does not work for me.

LspInstallInfo gives:

{                                                                                                                                
  julials = {
    is_installed = true
  }
}

with a .jl file open and invoking

lua print(vim.inspect(vim.lsp.buf_get_clients()))

gives

{}

So, certainly there is something wrong.
My init.vim looks like this:

call plug#begin('~/.local/share/nvim/plugged')                                                                                         

" nerdtree plugin for filemanagement
Plug 'scrooloose/nerdtree'

" airline nice statusbar
Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'
Plug 'ryanoasis/vim-devicons'

" collection of common configurations for the Nvim LSP client.
Plug 'neovim/nvim-lsp'

" Julia stuff
Plug 'JuliaEditorSupport/julia-vim'

" color scheme
Plug 'dracula/vim', { 'as': 'dracula' }

" fuzzy finder
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }

" deoplete
" Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' }

call plug#end()

lua << EOF
    require'nvim_lsp'.julials.setup{}
EOF

let g:deoplete#enable_at_startup = 0

" use built in neovim lsp for autocomplete
autocmd Filetype c,cpp,python,julia,vim setlocal omnifunc=v:lua.vim.lsp.omnifunc

"let g:LanguageClient_autoStart = 1
"let g:LanguageClient_serverCommands = {
"\   'julia': ['julia', '--startup-file=no', '--history-file=no', '-e', '
"\       using LanguageServer;
"\       server = LanguageServer.LanguageServerInstance(STDIN, STDOUT, false);
"\       server.runlinter = false;
"\       run(server);
"\   '],
"\ }

" change leader
let mapleader = "\<Space>"       | " Map leader to space
" let maplocalleader = "\\" | " Map localleader to \

" spaces instead of tabs
set tabstop=4
set shiftwidth=4
" always use spaces instead of \t
set expandtab
set autoindent
set smarttab

" higlight cursorline
set cursorline

" fix copy-paste buffers
set clipboard=unnamedplus

" enable true color
" note that one should enable true color in tmux if using tmux
set termguicolors

" syntax enbale
" colortheme and so on
" disable dracula italics because they look weird
" let g:dracula_italic = 0
colorscheme dracula
" airline theme
" let g:airline_theme='oceanicnext'
let g:airline_theme='dracula'
" airline powerline fonts, must be installed
let g:airline_powerline_fonts = 1

let g:airline#extensions#syntastic#enabled = 1
let g:airline#extensions#branch#enabled = 1
let g:airline#extensions#tabline#enabled = 1
let g:airline#extensions#tagbar#enabled = 1
let g:airline#extensions#virtualenv#enabled = 1
"let g:airline_skip_empty_sections = 1 " causes json to crash
let g:airline_section_c = '%t'

nnoremap <silent> <leader>ld    <cmd>lua vim.lsp.buf.declaration()<CR>
nnoremap <silent> <leader>lh    <cmd>lua vim.lsp.buf.hover()<CR>
nnoremap <silent> <leader>ld    <cmd>lua vim.lsp.util.show_line_diagnostics()<CR>
nnoremap <silent> <leader>lk    <cmd>lua vim.lsp.buf.signature_help()<CR>
nnoremap <silent> <leader>lr    <cmd>lua vim.lsp.buf.references()<CR> 

Do you see any immediate problem here?

Thanks!

Are you using neovim nightly?

Yes, I am using neovim nightly:

NVIM v0.5.0-478-g42b441738

I digged a little deeper and when I execute:

lua require('nvim_lsp').julia.setup{}

I get a lot of error messages like this:

E5108: Error executing lua .../flo/.local/share/nvim/plugged/nvim-lsp/lua/nvim_lsp.lua:77: module 'nvim_lsp/julia' not found:          
        no field package.preload['nvim_lsp/julia']                                                                                     
        no file '/home/flo/.config/nvim/lua/nvim_lsp/julia.lua'                                                                        
        no file '/home/flo/.config/nvim/lua/nvim_lsp/julia/init.lua'                                                                   
        no file '/home/flo/.local/share/nvim/plugged/nerdtree/lua/nvim_lsp/julia.lua'
...

So far so good :slight_smile:

Can you check on your computer what is in the nvim_lsp folder?

In:

/home/flo/.config/nvim/

is only my

init.vim

I do not even have a nvim_lsp folder.
I certainly messed something up with installation paths. I am using vim-plug and normally my plugins are located in:

~/.local/share/nvim/plugged

As you probably already have seen from my init.vim.

Here is the configuration for some capabilities of Julia’s LanguageServer.jl and Neovim’s built-in Language Server Protocol (LSP) client, and some instructions to install this setup.

I’m hoping people can try it with neovim and report issues if they come across it. The more people that use this, the better this functionality will get.

Capabilities

The .vimrc code corresponding to the capability is linked below the each screencapture.

Completion

vim.lsp.omnifunc

Documentation

vim.lsp.buf.hover

Jump to definition

vim.lsp.buf.definition

Linting

vim.lsp.util.show_line_diagnostics

References

vim.lsp.buf.references

Document symbols

vim.lsp.buf.document_symbol

Install

If you’d like to use this you will need the following:

  1. neovim v0.5.0
  2. neovim/nvim-lsp

The neovim/nvim-lsp repository contains language server configurations for a bunch of languages.
Once you have neovim/nvim-lsp installed with your favorite plugin manager, you can run :LspInstall julials.
That will download and install LanguageServer.jl and SymbolServer.jl into your global environment.
You may also want JuliaEditorSupport/julia-vim for syntax highlighting and other niceties.

At the moment you’ll have to make some changes to julials file. The changes required are in this PR: https://github.com/neovim/nvim-lsp/pull/258.

And, at the moment neovim v0.5.0 isn’t released yet. You’ll have to get the latest commit on master and build from source, or download a release from the nightly tag on github.
This also means that the lsp client is not stable yet.
And there may be bugs.

One such bug that I ran into is that LanguageServer.jl does not support textDocument/declaration and textDocument/typeDeclaration, but the neovim LSP client still sends the request if the user makes that request call.
This causes LanguageServer.jl to crash, and this looks like a silent failure from the user perspective.
This is resolved in this PR: https://github.com/neovim/neovim/pull/12421.
You can make the same changes locally to your runtime/lua/vim folder.

Here is a minimal .vimrc configuration that works with NVIM v0.5.0-539-g91e41c857.

set nocompatible
filetype off

if empty(glob('~/.local/share/nvim/site/autoload/plug.vim'))
  silent !curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs
    \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
  autocmd VimEnter * PlugInstall --sync | source $MYVIMRC
endif

call plug#begin('~/.local/share/nvim/plugged')

Plug 'JuliaEditorSupport/julia-vim'
Plug 'neovim/nvim-lsp'

call plug#end()

lua << EOF
    require'nvim_lsp'.julials.setup{}
EOF

autocmd Filetype julia setlocal omnifunc=v:lua.vim.lsp.omnifunc

nnoremap <silent> <c-]> <cmd>lua vim.lsp.buf.definition()<CR>
nnoremap <silent> K     <cmd>lua vim.lsp.buf.hover()<CR>
nnoremap <silent> gr    <cmd>lua vim.lsp.buf.references()<CR>
nnoremap <silent> g0    <cmd>lua vim.lsp.buf.document_symbol()<CR>

Once you have this, you should be able to open a .jl file and LanguageServer.jl will start up!
It may take some time for SymbolServer.jl to cache the symbols the first time you run it, so be prepared to wait for a while.
You can type :lua print(vim.lsp.get_log_path())<CR> in neovim to get the path to the language server log file.
When you see [ Info: Received new data from Julia Symbol Server. you should be good to go.

Originally posted over here: https://blog.kdheepak.com/neovim-languageserver-julia.html

11 Likes

Unfortunatly, I can not get this to work with my environment.
After doing all your changes I get the following error in the log file:
[ ERROR ] 2020-06-03T20:59:03Z+0200 ] …_nvim.aTYTtLi/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:317 ] “rpc” “julia” “stderr” “┌ Error: Exception while generating log record in module Main at none:9\n│ exception =\n│ BoundsError: attempt to access 0-element Array{String,1} at index [1]\n│ Stacktrace:\n│ [1] getindex(::Array{String,1}, ::Int64) at ./array.jl:744\n│ [2] top-level scope at logging.jl:320\n│ [3] eval(::Module, ::Any) at ./boot.jl:330\n│ [4] exec_options(::Base.JLOptions) at ./client.jl:263\n│ [5] _start() at ./client.jl:460\n└ @ Main none:9\n”

Any ideas?

Can you post a longer log? I don’t see what is going on here.

1 Like

That is unfortunatly everything I get when I open a julia file and look into the logfile given by: :lua print(vim.lsp.get_log_path())<CR>

I will try to give you more information after work.

Thanks for your helpful tutorial!

Thanks a lot for working on this. I did not succeed in setting it up correctly. I get:

|[ ERROR ] 2020-06-04T13:19:27+0200 ] ...nt_nvimeCvcQS/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:317 ]|"rpc"|"julia"|"stderr"|"MethodError: Cannot `convert` an object of type Bool to an object of type String\nClosest candidates are:\n  convert(::Type{String}, !Matched::FilePathsBase.AbstractPath) at /home/jan/.julia/packages/FilePathsBase/SZiBu/src/path.jl:107\n  convert(::Type{T}, !Matched::T) where T<:AbstractString at strings/basic.jl:229\n  convert(::Type{T}, !Matched::AbstractString) where T<:AbstractString at strings/basic.jl:230\n  ..."|
|---|---|---|---|---|
|[ ERROR ] 2020-06-04T13:19:27+0200 ] ...nt_nvimeCvcQS/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:317 ]|"rpc"|"julia"|"stderr"|"\nStacktrace:"|
|[ ERROR ] 2020-06-04T13:19:27+0200 ] ...nt_nvimeCvcQS/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:317 ]|"rpc"|"julia"|"stderr"|"\n [1] "|
|[ ERROR ] 2020-06-04T13:19:27+0200 ] ...nt_nvimeCvcQS/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:317 ]|"rpc"|"julia"|"stderr"|"LanguageServerInstance"|
|[ ERROR ] 2020-06-04T13:19:27+0200 ] ...nt_nvimeCvcQS/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:317 ]|"rpc"|"julia"|"stderr"|"("|
|[ ERROR ] 2020-06-04T13:19:27+0200 ] ...nt_nvimeCvcQS/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:317 ]|"rpc"|"julia"|"stderr"|"::"|
|[ ERROR ] 2020-06-04T13:19:27+0200 ] ...nt_nvimeCvcQS/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:317 ]|"rpc"|"julia"|"stderr"|"Base.PipeEndpoint"|
|[ ERROR ] 2020-06-04T13:19:27+0200 ] ...nt_nvimeCvcQS/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:317 ]|"rpc"|"julia"|"stderr"|", ::Base.PipeEndpoint, ::Bool, ::String, ::Nothing, ::Nothing) at "|
|[ ERROR ] 2020-06-04T13:19:27+0200 ] ...nt_nvimeCvcQS/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:317 ]|"rpc"|"julia"|"stderr"|"/home/jan/.julia/packages/LanguageServer/mpNvN/src/languageserverinstance.jl:62"|
|[ ERROR ] 2020-06-04T13:19:27+0200 ] ...nt_nvimeCvcQS/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:317 ]|"rpc"|"julia"|"stderr"|" (repeats "|
|[ ERROR ] 2020-06-04T13:19:27+0200 ] ...nt_nvimeCvcQS/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:317 ]|"rpc"|"julia"|"stderr"|"2 times)"|
|[ ERROR ] 2020-06-04T13:19:27+0200 ] ...nt_nvimeCvcQS/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:317 ]|"rpc"|"julia"|"stderr"|"\n [2] top-level scope at none:3\n|

Are you sure you are using this branch? https://github.com/neovim/nvim-lsp/pull/258

The current master branch passes the Bool value false as the third argument, which would cause this erro. But this PR fixes it. You’ll also need the latest version of LanguageServer and SymbolServer.

1 Like

Sorry I was not using that branch.

So I investigated a little.
In your fix for the julials file you have the lines
empty!(LOAD_PATH)
push!(LOAD_PATH, "@")
These lead to an empty Base.load_path() for me and this in turn than crashes with my posted error message, when I access Base.load_path()[1] in the @info line.

Nice debugging! Why is Base.load_path() empty if push!(LOAD_PATH, "@") is in there though? I based that code of this: https://github.com/non-Jedi/eglot-jl/blob/master/eglot-jl.jl

Can you try removing the the empty!(LOAD_PATH) and see if it works for you?

1 Like

Yeah, thanks that works for me!

Thanks a lot for your effort.

The only thing that is not working right now is the auto-complete.

Awesome guide and thanks for making the PR. I was banging my head against the wall with nvim and nvim-lsp which should support julia. I just couldn’t get it to work. Luckily with your patch it works. :slight_smile: The only trouble I have is the auto-complete which doesn’t seem to work on my end. Are you using any other ingredient than above to make that work?

Autocomplete might not be working for me as well actually. I am using neosnippets and I think that is somehow getting the LanguageServer.jl autocomplete suggestions but the default complete doesn’t appear to be filled in my configuration. I will look into it and post here if I find anything!

2 Likes

I’m able to get autocomplete working in scripts if I use the following configuration.

nvim -u MINRC test.jl
set nocompatible
filetype off

if empty(glob('~/.local/share/nvim/site/autoload/plug.vim'))
  silent !curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs
    \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
  autocmd VimEnter * PlugInstall --sync | source $MYVIMRC
endif

call plug#begin('~/.local/share/nvim/plugged')

Plug 'JuliaEditorSupport/julia-vim'
Plug '~/gitrepos/nvim-lsp'
Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' }
" Plug 'Shougo/deoplete-lsp'
let g:deoplete#enable_at_startup = 1

call plug#end()

lua << EOF
    require'nvim_lsp'.julials.setup{}
EOF

autocmd Filetype julia setlocal omnifunc=v:lua.vim.lsp.omnifunc

nnoremap <silent> <c-]> <cmd>lua vim.lsp.buf.definition()<CR>
nnoremap <silent> K     <cmd>lua vim.lsp.buf.hover()<CR>
nnoremap <silent> gr    <cmd>lua vim.lsp.buf.references()<CR>
nnoremap <silent> g0    <cmd>lua vim.lsp.buf.document_symbol()<CR>

But with nvim -u MINRC src/Package.jl it doesn’t seem to autocomplete correctly.

I’m still looking into it but thought I’d post this to see if someone else has the same effect.

Autocompletion is also working for me with deoplete-lsp and deoplete. There’s a open PR that needs to be merged for this to work properly.

2 Likes