Tuesday 28 February 2012

Emacs as IDE for Play! 2.0 framework

If you're here you are very likely to agree with me that those fancy IDEs like Eclipse, InteliJ, etc. are for babies, cos real
dinosaursprogrammersuse Emacs!
But using Emacs for Java or Scala seems like taking a few steps back in human race's development, right? Or maybe?...

ENSIME the ENhanced Scala Interaction Mode for Emacs

The ensime package is a set of software pieces written in elisp, Scala, and Java, and it looks promising. Here are the main features copied from the Readme.md file (along with some screenshots taken by me):
  • Highlight errors and warnings in your code buffers.
  • Inspect the type of any expression.
  • Browse packages
  • Completion for variables, methods, constructors, etc.
  • Incrementally search through classpath symbols
  • Find all references to a symbol
  • Jump to symbol definitions.
  • Semantic Highlighting
  • Automated Refactorings (rename, organize imports, extract method)
  • Source Formatting
  • AST-based selection
  • Supports sbt7,10,11
  • Supports Maven,Ivy build descriptions
  • Embedded sbt shell
  • REPL
  • Debug support
  • Linking to online Scaladoc

Installation

The easiest way to install it is to download the latest bundle from the downloads page and unpack it into your local ~/.emacs.d/plugins/ directory.
Next step is to enable loading the scala-mode and ensime in your .emacs file:
(add-to-list 'load-path (expand-file-name "~/.emacs.d/plugins/ensime/elisp"))
(add-to-list 'load-path (expand-file-name "~/.emacs.d/plugins/scala-mode"))
(when (and (require 'scala-mode-auto nil 'noerror) (require 'ensime nil 'noerror))
  (add-to-list 'auto-mode-alist '("\\.scala.html$" . scala-mode))
  (add-hook 'scala-mode-hook 'ensime-scala-mode-hook))

Then the sbt plugin has to be added to your projects app_name/project/plugins.scala file:
addSbtPlugin("org.ensime" % "ensime-sbt-cmd" % "VERSION")
The VERSION string should be replaced with the latest plugin version that can be read on the plugin project page in the README.md section.
At last the project should be (re)generated with the command:
ensime generate
that can be issued in the play console, and the project is ready to be 'connected' with the M-x ensime command in Emacs. Every time a library or Scala source is added to or removed from the project the command should be used. Otherwise you can hit an occasional 'symbol not found' message.

Memory usage

There is one issue that concerns me when using the ensime mode: the memory usage. When the project becomes 'connected' a background Java process is spawn, and that would be OK wasn't it for the 1.9GB of physical memory usage on a 64-bit, or about 1GB on a 32-bit Linux system for a simple project that does almost nothing except displaying the index page.
Exploring the ensime manual I came across the config file settings section describing the :only-include-in-index (regex*) that allowed me to cut by half the amount of physical memory eaten by the background process. The listing below shows Build.scala that helped me to achieve this effect:
import sbt._
import Keys._
import PlayProject._

import org.ensime.sbt.Plugin.Settings.ensimeConfig
import org.ensime.sbt.util.SExp._


object ApplicationBuild extends Build {

    val appName         = "dinobase"
    val appVersion      = "1.0-SNAPSHOT"

    val appDependencies = Seq(
      // Add your project dependencies here,
    )

    val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
      // Add your own project settings here
      ensimeConfig := sexp(
        key(":only-include-in-index"), sexp(
          "controllers\\..*",
          "models\\..*",
          "views\\..*",
          "play\\..*"
        )
      )
    )
}

Enable semantic highlighting

By default emacs uses syntactic highlighting in sources. Because a syntax highlighter cannot tell whether a symbol is val or var, or a method call it is recommended to enable the semantic highlighter by the snippet below to your .emacs file.
(setq ensime-sem-high-faces
      '(
        (var . (:foreground "#ff2222"))
        (val . (:foreground "#111111"))
        (varField . (:foreground "#ff6666"))
        (valField . (:foreground "#666666"))
        (functionCall . (:foreground "#84BEE3"))
        (param . (:foreground "#111111"))
        (class . font-lock-type-face)
        (trait . (:foreground "#084EA8"))
        (object . (:foreground "#026DF7"))
        (package . font-lock-preprocessor-face)))
Ofcourse some tunning may be neede to suit your taste.

Link to Play! sources and customize Scaladoc and Javadoc

According to the manual it should be possible to add the link to the Play! sources by adding appropriate keys to the ensimeConfig variable.
        key(":source-roots"), sexp(
          "/home/rajish/proj/scala-beginner/dinobase/app",
          "/home/rajish/proj/scala-beginner/dinobase/test",
          "/home/rajish/bin/play2/framework/src"
        )
But currently that takes no effect. When this issue gets fixed it should be managed in that way but for now you should tweak manually the value of the :source-roots key in the generated .ensime file.
Linking to Play! scaladoc should work when the following snippet is added to your .emacs file, but due to this issue it doesn't.
(defun make-play-doc-url (type &optional member)
  (ensime-make-java-doc-url-helper
    "file:///home/rajish/bin/play2/documentation/api/scala/" type member))

(add-to-list 'ensime-doc-lookup-map '("^play\\.api\\." . make-play-doc-url))
And currently there is no on-line scaladoc repository for the Play! framework, so we are forced to plant a local http server serving the documentation or resort to browsing the sources linked to the project (see previous paragraph).
It seems to be a better idea to put such settings to the project's .ensime file via customization in the Build.scala file. That's why I filled yet another issue.

Conclusion

First impression is very promising when the memory issue is minimized. I'll keep on using ensime for some time, and will report here when something worth noting (like an appearance of the new command emacsify) happen.

3 comments:

  1. Excellent article!

    Of course, real programmers use butterflies with one wing :)

    ReplyDelete
  2. So real programmers don't use IDEs, they use Emacs, with an Emacs plugin that replicates all the features that those dreadful IDEs provides...

    ReplyDelete
    Replies
    1. Sounds like you're not an Emacs user, aren't you? This post is for die-hard emacsers, and every die-hard emacser does not live outside Emacs (except for going to the toilet/lavatory/loo - one shouldn't litter the place of living.)

      FYI Emacs is built from 'plugins'. Without them you couldn't do any sensible thing in Emacs. And those 'plugins' make the editor(some of us call it OS or even God) awesome.

      Delete