DEV Community

Callum Lamont
Callum Lamont

Posted on

How to Setup Vim for Kotlin Development

The sad truth is that JVM languages are awkward to use with Vim. This is compounded for Kotlin. The user base is still smaller than Java's, so there are far fewer how-to guides to help set up your editor. Below should serve as a good crash course for developing Kotlin in Vim. Who needs to use IntelliJ ๐Ÿ’

So, to enable a reasonable Kotlin developer experience, we need to install:

  1. A Kotlin grammar file to enable syntax highlighting.
  2. The Kotlin language server (for IntelliSense and code navigation).
  3. A code completion engine (optional, for Neovim) to auto-import dependencies.

These instructions have been written primarily for Neovim, but includes alternatives if youโ€™d prefer to use the OG Vim.

Requirements

A Vim plugin manager.

  • You can manage plugins with Git submodules, but I've found this to be a painful experience.
  • Some examples are vim-plug, vundle, or, lazy.nvim.

An LSP client.

  • Neovim comes with a client. For Vim you will need to install one, such as CoC, LanguageClient-neovim, or vim-lsp.
  • Gradle.
  • Java 11+ and Kotlin. Currently there is an open issue for Kotlin versions greater than 2.0.0. Downgrade if you have any issues.

1. Syntax Highlighting

Because Kotlin isnโ€™t as mainstream as Python or Java, many editors donโ€™t have a default grammar to enable syntax highlighting.

Neovim

Neovim could use the default regex based grammar that Vim uses for syntax highlighting. But I recommended installing treesitter. This uses a concrete syntax tree to provide more semantic meaning to tokens, allowing enriched (and faster) highlighting.

Once treesitter is installed (using the instructions in the repository), install the Kotlin grammar.

:TSInstall kotlin
Enter fullscreen mode Exit fullscreen mode

And update your init.lua file to turn on treesitter synax highlighting.

local treesitter = require('nvim-treesitter.configs')
treesitter.setup {
  highlight = {
    enable = true,
    additional_vim_regex_highlighting = false,
  },
}
Enter fullscreen mode Exit fullscreen mode

You will have beautiful syntax highlighting when you next open Neovim ๐ŸŒˆ

Vim

kotlin-vim is a reasonably popular plugin that provides syntax highlighting, among other functionalities. This enables highlighting by providing Vim a file of regex based Kotlin grammar. Once installed, it should enable syntax highlighting without further configuration.

The usefulness of the syntax highlighting also depends on your chosen colourscheme. Have a look at this collection to find new schemes to try out!

2. The Language Server

Installing the Language Server

The Kotlin language server is packaged in few places, but I recommend to build it from the source code. This helps you understand how the server and client are orchestrated. It will also aid debugging if anything goes wrong.

First, clone the repository. I suggest keeping all language servers in one directory $HOME/language_servers.

$ cd $HOME/language_servers
$ git clone git@github.com:fwcd/kotlin-language-server.git
$ cd kotlin-language-server
Enter fullscreen mode Exit fullscreen mode

Check the build and tests work as expected.

$ ./gradlew clean build
Enter fullscreen mode Exit fullscreen mode

Build the project and binaries.

$ ./gradlew installDist
Enter fullscreen mode Exit fullscreen mode

There should now be an executable kotlin-language-server binary in $HOME/language_servers/build/install/bin.

For a large project, the Kotlin language server has a slow start up time (a few minutes) while it tokenises your entire project and stores these symbols in a database.

The server is also rather flakey. At some point while using it, it is likely to throw an error and stop working. Just close Vim and reopen your buffers again ๐Ÿ’

Configuring the LSP Client

Neovim
Install the nvim-lspconfig plugin to make configuration of the LSP client simpler. Then update your init.lua to tell Neovim which filetypes to use the language server for (i.e. .kt and .kts extensions). The LSP client will try to the start the sever by running the kotlin_language_server binary. You can directly point Neovim to the location of the binary, or include this directory in your $PATH.

local lsp = require('lspconfig')
lsp.kotlin_language_server.setup{
  filetypes = { "kotlin" , "kt", "kts"},
  -- If you don't update you $PATH
  cmd = { os.getenv( "HOME" ) .. "/language_servers/build/install/bin/kotlin_language_server" },
}
Enter fullscreen mode Exit fullscreen mode

Vim
Follow the configuration instructions on the GitHub repo of your chosen LSP client (each one will be different). The Kotlin language server repository also provides some sample configuration for CoC and LanguageClient-neovim.

3. Completion Engine (Neovim only)

The builtin Vim omni completion engine is simple, but surprisingly useful. It will allow you to autocomplete any existing word in your open buffers (which is sufficient for a lot of use cases). But the completion menu wonโ€™t include options from your project dependencies.

With a language server set up, we have broader, project-wide context at our disposal. If we can plug this intelligence into our completion engine, it would then be possible to autocomplete and auto-import any method or class that exists in our project or its dependencies.

For this, we need to install a new completion engine: nvim-cmp. This engine uses completion โ€œsourcesโ€ (installed as separate dependencies) to populate the completion menu. So we also need to install cmp-buffer and cmp-nvim-lsp. Check out this tutorial by TJ DeVries for more info about how this plugin works.

Once those three plugins are installed, add this configuration to your init.lua. See the repository for more configuration options.

cmp.setup {
  mapping = {
    ["<C-n>"] = cmp.mapping.select_next_item { behavior = cmp.SelectBehavior.Insert },
    ["<C-p>"] = cmp.mapping.select_prev_item { behavior = cmp.SelectBehavior.Insert },
    ['<C-u>'] = cmp.mapping.scroll_docs(-4),
    ['<C-d>'] = cmp.mapping.scroll_docs(4),
    ['<C-o>'] = cmp.mapping.complete_common_string(),
    ['<C-e>'] = cmp.mapping.abort(),
    ['<C-y>'] = cmp.mapping.confirm(),
  },
  sources = {
    { name = 'buffer', keyword_length = 3 },  -- only start autocompleting after a few chars typed
    { name = 'nvim_lsp', max_item_count = 10 },  -- don't overpopulate list with symbols from LSP
  },
  -- Just for aesthetics
  window = {
    completion = cmp.config.window.bordered(),
    documentation = cmp.config.window.bordered(),
  },
}
Enter fullscreen mode Exit fullscreen mode

Now, when you hit CTRL-n will open a completion menu. Pressing CTRL-y on an option will autocomplete with that symbol and (if required) insert the needed import statement at the top of your file ๐ŸŽ‰

Wrap up

Following the above configuration should provide you syntax highlighting, LSP functionality (diagnostics, go to definition, etc.), and auto import.
Kotlin development, in action

Even with the above configuration, developing Kotlin in Vim isn't the smoothest experience. This is mostly due to limitations with the Kotlin LSP. I hope community support will improve this over time ๐Ÿ™‚

Here is a fully functioning init.lua file for Neovim that includes all the configuration for the the above plugins.

Top comments (0)