While it’s possible to code Scala with just an editor and sbt, most programmers today use an Integrated Development Environment, or IDE for short. Two of the popular IDEs in Scala are Metals and IntelliJ IDEA, and they both integrate with sbt builds.
Metals is an open source language server for Scala, which can act as the backend for VS Code and other editors that support LSP. Metals in turn supports different build servers including sbt via the Build Server Protocol (BSP).
To use Metals on VS Code:
build.sbt
file.
Cmd-Shift-P
on macOS) “Metals: Switch build server”, and select “sbt”Use the following setting to opt-out some of the subprojects from BSP.
bspEnabled := false
When you make changes to the code and save them (Cmd-S
on macOS), Metals will invoke sbt to do
the actual building work.
See Debugging page on VS Code documentation for more details on how to navigate an interactive debugging session.
While Metals uses sbt as the build server, we can also log into the same sbt session using a thin client.
sbt --client
This lets you log into the sbt session Metals has started. In there you can call testOnly
and other tasks with
the code already compiled.
IntelliJ IDEA is an IDE created by JetBrains, and the Community Edition is open source under Apache v2 license. IntelliJ integrates with many build tools, including sbt, to import the project. This is a more traditional approach that might be more reliable than using BSP approach.
To import a build to IntelliJ IDEA:
build.sbt
file.IntelliJ Scala plugin uses its own lightweight compilation engine to detect errors, which is fast but sometimes incorrect. Per compiler-based highlighting, IntelliJ can be configured to use the Scala compiler for error highlighting.
See Debug Code page on IntelliJ documentation for more details on how to navigate an interactive debugging session.
Importing the build to IntelliJ means that you’re effectively using IntelliJ as the build tool and the compiler while you code (see also compiler-based highlighting). While many users are happy with the experience, depending on the code base some of the compilation errors may be false, it may not work well with plugins that generate sources, and generally you might want to code with the identical build semantics as sbt. Thankfully, modern IntelliJ supports alternative build servers including sbt via the Build Server Protocol (BSP).
The benefit of using BSP with IntelliJ is that you’re using sbt to do the actual build work, so if you are the kind of programmer who had sbt session up on the side, this avoids double compilation.
Import to IntelliJ | BSP with IntelliJ | |
---|---|---|
Reliability | ✅ Reliable behavior | ⚠️ Less mature. Might encounter UX issues. |
Responsiveness | ✅ | ⚠️ |
Correctness | ⚠️ Uses its own compiler for type checking, but can be configured to use scalac | ✅ Uses Zinc + Scala compiler for type checking |
Generated source | ❌ Generated source requires resync | ✅ |
Build reuse | ❌ Using sbt side-by-side requires double build | ✅ |
To use sbt as build server on IntelliJ:
Cmd-Shift-P
on macOS) and
type “Existing” to find “Import Project From Existing Sources”:build.sbt
file. Select BSP when prompted:Use the following setting to opt-out some of the subprojects from BSP.
bspEnabled := false
When you make changes to the code and save them (Cmd-S
on macOS), IntelliJ will invoke sbt to do
the actual building work.
See also Igal Tabachnik’s Using BSP effectively in IntelliJ and Scala for more details.
We can also log into the existing sbt session using the thin client.
sbt --client
This lets you log into the sbt session IntelliJ has started. In there you can call testOnly
and other tasks with
the code already compiled.
Neovim is a modern fork of Vim that supports LSP out-of-box, which means it can be configured as a frontend for Metals.
Chris Kipp, who is a maintainer of Metals, created nvim-metals plugin that provides comprehensive Metals support on Neovim. To install nvim-metals, create lsp.lua
under $XDG_CONFIG_HOME/nvim/lua/
based on Chris’s lsp.lua and adjust to your preference. For example, comment out its plugins section and load the listed plugins using the plugin manager of your choice such as vim-plug.
In init.vim
, the file can be loaded as:
lua << END
require('lsp')
END
Per lsp.lua
, g:metals_status
should be displayed on the status line, which can be done using lualine.nvim etc.
:MetalsInstall
when prompted.
:MetalsStartServer
.
gD
(exact keybinding can be customized):Ctrl-O
to return to the old buffer.
K
in Normal mode:<leader>aa
::cnext
and :cprev
to nagivate through the errors and warnings.
<leader>ae
.
<leader>dt
:K
), and then
“debug continue” (<leader>dc
) to start a debugger.
Choose “1: RunOrTest” when prompted.
<leader>dK
):<leader>dc
) again to end the session.
See nvim-metals regarding further details.
We can also log into the existing sbt session using the thin client.
:terminal
to start the built-in terminal.
sbt --client
Even though it’s inside Neovim, tab completion etc works fine inside.