Vim Setup For Scala

Sep 13, 2017

I personally choose to work in Vim whenever possible, and have spent some time getting a smooth experience with Scala. Like with Bash and Unix tools, I enjoy reading about other developers’ workflows and tips, and hopefully mine help you!

My flavor of Vim is Neovim and my full init.vim + dotfiles are over on Github. For working in Scala in Vim, I use:

1. Syntax

vim-scala, not much to say there.

2. Completion

I use deoplete, and it has worked without issue. However, I don’t have strong preferences here and there are many other options in Vim.

2. Jump to File

FZF works really well and really fast. I used to use ctrlp but it struggled with the size of the codebase I worked in, and FZF works really well with tags. I still fall back onto ctrlp on machines where I don’t want to have to install the FZF binary.

3. Jump to definition

ctags + FZF can get you quite far for jumping to definition. If you’ve never used tags before, imagine searching for definitions (e.g. def ([a-zA-Z0-9]*), along with objects, traits, etc) across your codebase, recording the file and line where they occur. You then have an index of definitions that you can quickly search across later. This is all tags are, and FZF provides fuzzy search on top of that.

Here’s how I’ve set it up:

" FZF / Ctrlp for file navigation
if executable('fzf')
  Plug '/usr/local/opt/fzf'
  Plug 'junegunn/fzf.vim'
else
  Plug 'ctrlpvim/ctrlp.vim'
endif

" Ripgrep for file indexing, sort of faster, but not really, but also why not use ripgrep for
everything
if executable('rg')
  let $FZF_DEFAULT_COMMAND = 'rg --files --no-messages "" .'
endif

" Use FZF for files and tags if available, otherwise fall back onto CtrlP
" <leader>j will search for tag using word under cursor
let g:fzf_command_prefix = 'Fzf'
if executable('fzf')
  nnoremap <leader>v :FzfFiles<cr>
  nnoremap <leader>u :FzfTags<cr>
  nnoremap <leader>j :call fzf#vim#tags("'".expand('<cword>'))<cr>
else
  nnoremap <leader>v :CtrlP<Space><cr>
endif

For ctags, you also need to set up your ~/.ctags file (below is from Derek Wyatt’s), and on OSX you need to install a newer version of ctags than the preinstalled version:

--langdef=scala
--langmap=scala:.scala

--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy)[ \t]*)*(private[^ ]*|protected)?[ \t]*class[ \t]+([a-zA-Z0-9_]+)/\4/c,classes/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy)[ \t]*)*(private[^ ]*|protected)?[ \t]*object[ \t]+([a-zA-Z0-9_]+)/\4/o,objects/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy)[ \t]*)*(private[^ ]*|protected)?[ \t]*((abstract|final|sealed|implicit|lazy)[ \t ]*)*case class[ \t ]+([a-zA-Z0-9_]+)/\6/C,case classes/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy)[ \t]*)*(private[^ ]*|protected)?[ \t]*case object[ \t]+([a-zA-Z0-9_]+)/\4/O,case objects/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy)[ \t]*)*(private[^ ]*|protected)?[ \t]*trait[ \t]+([a-zA-Z0-9_]+)/\4/t,traits/
--regex-scala=/^[ \t]*type[ \t]+([a-zA-Z0-9_]+)/\1/T,types/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy|override|private[^ ]*(\[[a-z]*\])*|protected)[ \t]*)*def[ \t]+([a-zA-Z0-9_]+)/\4/m,methods/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy|override|private[^ ]*|protected)[ \t]*)*val[ \t]+([a-zA-Z0-9_]+)/\3/V,values/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy|override|private[^ ]*|protected)[ \t]*)*var[ \t]+([a-zA-Z0-9_]+)/\3/v,variables/
--regex-scala=/^[ \t]*package[ \t]+([a-zA-Z0-9_.]+)/\1/p,packages/

Afterwards this looks like:

FZF And ctags in Neovim

4. Jump to error

I’ve had success with Sarsi. I’ll start up sbt with sarsi-sbt, and then running neovim from the same directory, sbt errors show up as quickfix errors. This works well if you’re already keeping sbt in a separate window/tmux pane, but maybe you’d prefer something like sbt-vim that just keeps sbt in the background.

I also use vim-togglelist to toggle the quickfix window with <leader>q. <leader>l is mapped to the location list that other plugins use, like Neomake, which I use for rust.

5. Searching & Finding Usages

Ripgrep holds up to its reputation of being ridiculously fast, and I have alias rgs=rg -i -tscala for search Scala. I also mapped it as a command in vim :Rg but I rarely use this because the number of results that will populate the window.

Other

Debugging & Types

Most of the time, I get by using println for debugging and putting in a bogus type to see what the type error is:

// Poor man's type inference
scala> val x: Int = PooledHttp1Client()
<console>:20: error: type mismatch;
 found   : org.http4s.client.Client
 required: Int
       val x: Int = PooledHttp1Client()

Other Tools

I’ve also seen others using

IntelliJ

Even if you prefer life in an IDE like IntelliJ, Vim can be a quick way of navigating and exploring projects. But for some things, I find Vim is not enough. I do occasionally work in IntelliJ, and can appreciate