DEV Community

Cover image for Haskell LSP (bonus: for Vim)

Posted on

Haskell LSP (bonus: for Vim)

So you enjoy coding in Haskell. And you heard kids nowadays code using this LSP thingy.

Let's cut to the chase: LSP is a spec that standardizes auto-completion, code navigation, linting, and all good stuff usually found in modern IDE's. In order to use LSP with your favorite language you'll need a language server to communicate with your editor of choice.

In this article we'll focus on haskell's language server. Not long ago the language server of choice used to be hie but now it's deprecated.

So let's get started! Assuming you have Haskell Stack up and running:

$ stack install ghcid hspec-discover # optional but great
$ git clone --recurse-submodules
$ cd haskell-language-server
$ stack ./install.hs help
$ stack ./install.hs hls
Enter fullscreen mode Exit fullscreen mode

The binaries we'll be at the usual place: ~/.local/bin

Right, but how do I use it? Well it depends on your editor. Let me show you how I do it in my .vimrc:

Plug 'prabirshrestha/vim-lsp'
Plug 'prabirshrestha/asyncomplete.vim'
Plug 'prabirshrestha/asyncomplete-lsp.vim'
Plug 'mattn/vim-lsp-settings'
Enter fullscreen mode Exit fullscreen mode

Please read their documentation!

" vim-lsp-settings won't detect hls automatically as of today (2020-10-26). Let's teach it:
if (executable('haskell-language-server-wrapper'))
  au User lsp_setup call lsp#register_server({
      \ 'name': 'haskell-language-server-wrapper',
      \ 'cmd': {server_info->['haskell-language-server-wrapper', '--lsp']},
      \ 'whitelist': ['haskell'],
      \ })

" My Mappings
function! s:on_lsp_buffer_enabled() abort
    setlocal omnifunc=lsp#complete
    setlocal signcolumn=yes
    if exists('+tagfunc') | setlocal tagfunc=lsp#tagfunc | endif
    nmap <buffer> gd <plug>(lsp-definition)
    nmap <buffer> gr <plug>(lsp-references)
    nmap <buffer> gf <plug>(lsp-code-action)
    nmap <buffer> gi <plug>(lsp-implementation)
    nmap <buffer> gt <plug>(lsp-type-definition)
    nmap <buffer> <F2> <plug>(lsp-rename)
    nmap <buffer> [g <Plug>(lsp-previous-diagnostic)
    nmap <buffer> ]g <Plug>(lsp-next-diagnostic)
    nmap <buffer> K <plug>(lsp-hover)
    xmap <buffer> f <plug>(lsp-document-range-format)
    nmap <buffer> <F5> <plug>(lsp-code-lens)

    " buffer format on save
    " autocmd BufWritePre <buffer> LspDocumentFormatSync

" Decorations
augroup lsp_install
    let g:lsp_signs_enabled = 1         " enable signs
    let g:lsp_diagnostics_echo_cursor = 1 " enable echo under cursor when in normal mode
    let g:lsp_signs_error = {'text': 'βœ—'}
    " let g:lsp_signs_warning = {'text': 'β€Ό', 'icon': '/path/to/some/icon'} " icons require GUI
    " let g:lsp_signs_hint = {'icon': '/path/to/some/other/icon'} " icons require GUI
    let g:lsp_signs_warning = {'text': 'β€Ό'}
    let g:lsp_highlight_references_enabled = 1
    highlight link LspErrorText GruvboxRedSign " requires gruvbox
    highlight clear LspWarningLine
    " highlight lspReference ctermfg=red guifg=red ctermbg=green guibg=green
    highlight lspReference guibg=#303010

    " call s:on_lsp_buffer_enabled only for languages that has the server registered.
    autocmd User lsp_buffer_enabled call s:on_lsp_buffer_enabled()
augroup END
Enter fullscreen mode Exit fullscreen mode

The first time you start vim with a Haskell file it'll take a while (you can check vim-lsp's status with :LspStatus). As soon as a left margin (1 character wide) shows up, you're good to go!

Now your Vim is an LSP powerhouse:

  • Hover:

Alt Text

  • Auto-complete:

Alt Text

etc. etc. etc.


Top comments (6)

wayneseymour profile image

Hello there, I've been using vim since about 1999 but never really dug into the internals. I added what you have to my ~/.vimrc, but not sure where to save the next block you added to. Ideas?

moniquelive profile image

Hi, I've never tried this solution on Windows, if it works for you let me know! :)
The first block (with "Plug..." lines) assumes you have installed the vim-plug manager. This manager has a configuration block delimited by calls to plug#begin and plug#end.
The second block of the post (the one with the maps) can be pasted anywhere after the call to plug#end.
If you don't use vim-plug, take a look at it here

Good luck!

wayneseymour profile image

Hey thanks but I'm on MAC, not windows. :). Thanks for the plug for plug lol

Thread Thread
moniquelive profile image

I'm sorry! I don't know where I read that... 😊
I use this on Mac and Linux and it works fine.

  • M
Thread Thread
wayneseymour profile image

Sweet! Thanks mate!

ysangkok profile image
Janus Troelsen

You can install hls with ghcup too.