Vim and Haskell in 2019
I have recently decided to update my vim configuration for Haskell which had been set up back in 2017 following the instructions from the “Vim and Haskell in 2016” blog post. I was pleasantly surprised to find out that things seem to have evolved quite a bit since then, and that Haskell in vim is now pretty feature rich.
(Neo)Vim
First, let me clear up that I’m not actually using vim, but instead I’m using neovim. For those not familiar with neovim, it’s a fork of vim that had a bit of a code cleanup and was initially focused on being a version of vim with support for asynchronous plugins. At this point in time, vim 8 has been out for a while and the asynchronous plugin support has stopped being an issue in vim, but there’s still some interesting features in neovim, like floating windows, available at the moment only in the nightly builds, this is going to be required, in order to get the full functionality that I’ll be describing here.
I’m running neovim 0.4.0-dev
, the way to get these nightly builds on your
system varies with which system you’re running, instructions for different
systems can be found on
neovim’s documentation.
Haskell Tools
The Haskell tooling ecosystem is very rich, and vim plugins make use of those tools to provide functionality. Here’s a list of the tools we want to have installed, in our path, in order for those plugins to work:
- haskell ide engine (
hie
andhie-wrapper
) (installation instructions) hlint
(installation instructions)- apply-refact (
refactor
) (installation instructions) hindent
orstylish-haskell
(hindent installation instructions, stylish-haskell installation instructions)
Typically you’ll install these tools by running stack install <tool name>
,
but to be on the safe side, follow the instructions on the tool’s
documentation. Haskell Ide Engine specifically, has a slightly different
installation process and hie-wrapper
won’t work correctly unless it is
compiled with the same version of ghc
as the project you’re using it with.
Vim plugins
Once these tools are up and running, we now need to set up the required vim plugins to get the most out of vim while editing Haskell projects. We’re going to be using the following plugins:
neoclide/coc.nvim
neovimhaskell/haskell-vim
alx741/vim-hindent
oralx741/vim-stylishask
(optional)mpickering/hlint-refactor-vim
I’m not going to provide specific instructions on how to install each plugin
as those instructions can be found on each plugin’s page. The instructions
are also very simple, and will depend on which plugin manager you’re using. I
personally prefer to use vim plugger so, assuming I have plugger installed and
configured, I just add the following to my configuration, then reload the
configuration and run PlugUpdate
:
Plug 'neoclide/coc.nvim', {'do': { -> coc#util#install()}}
Plug 'neovimhaskell/haskell-vim'
Plug 'alx741/vim-hindent' " Optional
" Plug 'mpickering/hlint-refactor-vim'
coc.vim
coc.vim is described as “an intellisense engine for vim8 & neovim”, it will
use hie
as the backend to provide intelligent suggestions, code navigation and
show errors.
You’ll need to set up hie
as the language server, Haskell specific
instructions on how to configure coc.vim can be found
here and
you can find and example configuration containing useful keybinding for
navigating errors, going to definition, showing documentation/type under the
cursor, etc,
here.
Bellow is an example of coc.vim’s functionality for showing the type under the
cursor, the keybinding, if you use the example configuration linked above,
will be K
:
As you can see in the screenshot, it gives you not just the declared type but also the inferred type for the current context.
To also get linting, you’ll need to change to true
the hlintOn
setting which in
the example configuration is set to false
. So, in vim do :CocConfig
, which
will open coc.vim’s configuration file, and change the Haskell language server
configuration to:
"haskell": {
"command": "hie-wrapper",
"rootPatterns": [".stack.yaml", "cabal.config", "package.yaml"],
"filetypes": ["hs", "lhs", "haskell"],
"initializationOptions": {},
"settings": {
"languageServerHaskell": {
"hlintOn": true,
"maxNumberOfProblems": 10,
"completionSnippetsOn": true
}
}
}
Now you should be able to see linting suggestions, which we should be able to
automatically apply once we install hlint-refactor-vim
:
haskell-vim
haskell-vim gives you a more Haskell and contextually aware syntax highlighting. It’s worth it to be aware that not every theme will play well with this plugin, but most quality themes will provide a complete colour configuration and therefore work well with haskell-vim.
vim-hindent or vim-stylishask (Optional)
You’ll only require any of these if you want to follow the coding standards
enforced by these tools. Alternatively, you can skip these completely and use
coc.vim’s formatting feature. A keybiding for :call CocAction('format')
is
all that’s required:
nnoremap <leader> F :call CocAction('format')<CR>
If for some reason you’d prefer to use vim-hindent
or stylish-haskell
instead, these plugins use hindent
and stylish-haskell
to auto-format the
file on save.
hlint-refactor-vim
Turns out you can get coc.vim to do refactoring for you without the need for
an extra plugin. This was pointed out to me on reddit.
Setting up a keybiding for the coc-fix-current
action, would do the trick:
nmap <leader>qf <Plug>(coc-fix-current)
This plugin uses hlint’s refactor functionality to apply the suggestions made
by hlint to the current file, or the code under the cursor. By default to
will apply changes to the code under the cursor, and ta
will apply all the
suggested changes in the file.
Other plugins
I use a number of other plugins that provide me with common functionality that is also useful when writing Haskell, but that are not specifically related to writing Haskell. The list of all plugins I use can be found here.
Conclusion
And that’s it really, with these plugins, and some basic configuration, I have smart code completion, smart code navigation, information about the code and types, error, linting and automatic refactoring, all without leaving vim.
All in all, Haskell with vim in 2019 is in a pretty good place.
If you'd like to keep up to date with new posts, follow me on twitter @mpmlopes ,
or subscribe to the feed.