Scala Support in Doom Emacs With metals-emacs

February 06, 2020

Language support for Scala is provided for by metals-emacs (Ensime seems to have been officially deprecated a few months ago).

Installing metals-emacs with Coursier

The installation instructions in the Metals documentation didn’t work out of the box. I added verbose flags to try and debug, but the output didn’t lead anywhere.

# first, let's check where this redirects to
$ curl -sI https://git.io/coursier | grep Location
Location: https://github.com/coursier/coursier/raw/master/coursier

$ curl -L -o coursier https://git.io/coursier
$ chmod +x coursier
$ ./coursier bootstrap \
  --java-opt -Xss4m \
  --java-opt -Xms100m \
  --java-opt -Dmetals.client=emacs \
  org.scalameta:metals_2.12:0.8.0 \
  -r bintray:scalacenter/releases \
  -r sonatype:snapshots \
  -o /usr/local/bin/metals-emacs -f -v -v -v

-- snip --

Found main classes:
  org.h2.tools.Console (vendor: , title: )
  org.xnio.Version (vendor: org.jboss.xnio, title: XNIO API)
  com.sun.jna.Native (vendor: , title: Java Native Access (JNA))
  scala.meta.metals.Main (vendor: org.scalameta, title: metals)
  org.xnio.Version (vendor: org.jboss.xnio, title: XNIO NIO Implementation)
  bloop.launcher.Launcher (vendor: ch.epfl.scala, title: bloop-launcher-core)

  Fetching artifacts
  Found 78 artifacts
  Ignoring error(s):

  Ignoring error(s):

Error while writing /usr/local/bin/metals-emacs (/usr/local/bin/metals-emacs)

I went over to the Coursier documentation to check for clues. Using the native launcher expressedly for Linux didn’t work either:

$ curl -sI https://git.io/coursier-cli-linux | grep Location
Location: https://github.com/coursier/launchers/raw/master/cs-x86_64-pc-linux

$ curl -Lo cs https://git.io/coursier-cli-linux
$ chmod +x cs
$ ./cs  bootstrap \
  --java-opt -Xss4m \
  --java-opt -Xms100m \
  --java-opt -Dmetals.client=emacs \
  org.scalameta:metals_2.12:0.8.0 \
  -r bintray:scalacenter/releases \
  -r sonatype:snapshots \
  -o /usr/local/bin/metals-emacs -f -v -v -v


  Dependencies:

  Result:
Found main classes:


Exception in thread "main" coursier.cli.launch.LaunchException$NoMainClassFound: Cannot find default main class
        at coursier.cli.bootstrap.Bootstrap$.$anonfun$task$6(Bootstrap.scala:85)
        at coursier.cli.bootstrap.Bootstrap$.$anonfun$task$6$adapted(Bootstrap.scala:74)
        at coursier.util.Task$.$anonfun$flatMap$2(Task.scala:14)
        at scala.concurrent.Future.$anonfun$flatMap$1(Future.scala:307)
        at scala.concurrent.impl.Promise.$anonfun$transformWith$1(Promise.scala:41)
        at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:64)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
        at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:497)
        at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:193)

Luckily, the JAR-based launcher does:

$ curl -sI https://git.io/coursier-cli | grep Location
Location: https://github.com/coursier/coursier/raw/gh-pages/coursier

$ curl -Lo coursier2 https://git.io/coursier-cli
$ chmod +x coursier2
$ ./coursier2 bootstrap \
  --java-opt -Xss4m \
  --java-opt -Xms100m \
  --java-opt -Dmetals.client=emacs \
  org.scalameta:metals_2.12:0.8.0 \
  -r bintray:scalacenter/releases \
  -r sonatype:snapshots \
  -o /usr/local/bin/metals-emacs -f -v -v -v
  
-- snip --

  100.0% [##########] 525.9 KiB (981.2 KiB / s)
https://repo1.maven.org/maven2/com/h2database/h2/1.4.200/h2-1.4.200.jar
  100.0% [##########] 2.2 MiB (1.3 MiB / s)
https://repo1.maven.org/maven2/org/scalameta/parsers_2.12/4.3.0/parsers_2.12-4.3.0.jar
  100.0% [##########] 943.1 KiB (1.0 MiB / s)
https://repo1.maven.org/maven2/org/scalameta/metals_2.12/0.8.0/metals_2.12-0.8.0.jar
  100.0% [##########] 3.8 MiB (395.5 KiB / s)
Wrote /usr/local/bin/metals-emacs

I didn’t dig deeper, but the JAR-based launcher seems to be a later version:

$ ./coursier --help
Coursier 1.1.0-M9
-- snip --
$ ./coursier2 --help
Coursier 2.0.0-RC6-1
-- snip --

Doom Emacs setup

Once metals-emacs is in your path, simply enable support for it in Doom Emacs by:

  1. First enabling lsp, if you haven’t,
;; .doom.d/init.el
       :tools
        -- snip --
       ;;flyspell          ; tasing you for misspelling mispelling
       ;;gist              ; interacting with github gists
       (lookup           ; helps you navigate your code and documentation
        +docsets)        ; ...or in Dash docsets locally
       lsp
       ;;macos             ; MacOS-specific commands
       magit             ; a git porcelain for Emacs
       make              ; run make tasks from Emacs
       ;;pass              ; password manager for nerds
       ;;macos             ; MacOS-specific commands
  1. then adding the +lsp flag to the scala module.
       ;;ruby              ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
       ;;rust              ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
       (scala
        +lsp)             ; java, but good
       sh                ; she sells {ba,z,fi}sh shells on the C xor
       ;;solidity          ; do you need a blockchain? No.
       ;;swift             ; who asked for emoji variables?

and everything (lsp-mode and all its attendant functionality) should just work. You don’t have to follow any of these instructions.

company-complete
company-complete

lsp-ui-doc-show
lsp-ui-doc-show

lsp-describe-thing-at-point
lsp-describe-thing-at-point

(Tested on Doom Emacs v2.0.9 and Coursier 2.0.0-RC6-1.)