First Cup of Emacs - Configuration
Last updated on Feb 26, 2017
14 minutes read

Journey Begins

Some time ago I found an editor I considered being a great tool. I’ve loved text editors since I was a kid. I even played with Turbo Pascal to write my own one. When I switched to Mac and Ruby I appreciated Textmate. It was so great and lightweight compared to Netbeans, Eclipse, or Visual Studio.

Next big discovery was Vim and a bunch of Unix tools supporting stuff even better than IDEs. Moreover, I put a lot of effort to utilize my Vim based environment including some of Vim plugins that people still find useful.

And then I got Emacs. I should mention, there was a gap between Vim and Emacs when I switched back to Java/Scala and poked with IntelliJ IDEA and Atom. There are a large number of key bindings and editing habits related to text editors. The world has been divided between Vim, Emacs, and the rest. I guess moving from Vim directly to Emacs might be even harder than relearning the rest first.

But of course, it’s still possible.

Cosmic Emacs

One of my biggest fears regarding Emacs was that it would take me years to set it up in a sensible way. Emacs and Vim are quite old citizens in IT industry. By default, they require lots of customization to integrate with a modern OS and just look cute. It took a lot of time to craft my .vimrc and a robust plugin set.

The raw Emacs doesn’t appear friendly to anyone. But I found two distributions containing suitable sets of plugins and settings. The first one was Spacemacs. It was an answer for Vim camp refugees who were a bit lost in a world without Vim modes. I spent some time playing around so-called Holy mode (contrary to the Evil Vi mode). The Holy mode has been designed to provide a real Emacs experience. It wasn’t bad, but eventually, Spacemacs seemed to be built around the Evil. I couldn’t easily disable or remove the majority of plugins meant just to support Evil side. Since I didn’t need all those plugins and configuration quirks, I continued to look for a more appropriate Emacs distro.

Orchestral Emacs

Finally, I have found Prelude. It’s a solution prepared and maintained by Bozhidar Batsov - a hacker well known in Ruby community by his outstanding coding conventions and many projects. Indeed, Prelude was very well thought and focused on developer’s perspective. You could feel the less is more approach that was very close to my preferences.

In this tutorial, I’m going to explain Prelude and its configuration. To install Prelude, I’d recommend you to fork Prelude’s repository, clone it, and adjust to your needs. In that way, everything would be clean and easy to maintain. I’ll follow this path in the post and provide you more details regarding my settings.

Ensime, Scala, and Miscellaneous Stuff

This guide assumes a Scala developer’s point of view. Therefore, we need to start some preparations before enjoying the editor itself. I also assume we use macOS Sierra and Homebrew. You can skip Scala part if you are interested only in my Emacs setup.

1. Bind Caps Lock to Control

A must have one. There is a lot of info on the Internet about customization of key bindings for Emacs. You can try out various configurations, but this one is the only one I strongly recommend.

Go to your System Preferences/Keyboard/Modifier Keys and choose ^ Control for Caps Lock. You can also adjust the speed of repetitions to make your keyboard more snappy. It’s important as in Emacs you’re going to use keyboard extensively.

I’d recommend setting maximum speed of repetitions and shortest delay before them.

2. Install Scala and SBT (assuming you have JDK already installed)

brew update
brew upgrade
brew install scala
brew install sbt

3. Install Ensime plugin for SBT

Create a directory plugins and a file plugins.sbt in ~/.sbt/0.13:

mkdir -p ~/.sbt/0.13/plugins
touch ~/.sbt/0.13/plugins/plugins.sbt

Then edit that file ~/.sbt/0.13/plugins/plugins.sbt and put inside:

addSbtPlugin("org.ensime" % "sbt-ensime" % "1.12.5")

We also need an option preventing the Ensime plugin from generating unwanted directories.

touch ~/.sbt/0.13/global.sbt

Then edit and put there:

import org.ensime.EnsimeCoursierKeys._
import org.ensime.EnsimeKeys._

ensimeIgnoreMissingDirectories := true

Now you should run sbt ensimeConfig in your SBT project. If you are a beginner, don’t worry. I will write about setting up a new SBT project in another blog post. ensimeConfig command generates a .ensime file that Ensime would use internally.

4. Install some tools that make Emacs installation smooth

Install a Markdown utility:

brew install markdown

For the first time you open a Markdown file in Emacs, it will download some necessary packages from Melpa, so be patient.

Install Aspell dictionary:

brew install aspell --with-lang-en

Install Ag search utility:

brew install ag

Installation

First, we need to install Emacs itself:

brew install emacs --with-cocoa --with-gnutls --with-librsvg --with-imagemagick
brew linkapps emacs

Next, we need to install Prelude in ~/.emacs.d directory. The recommended way is to clone Prelude’s repository (or even fork it first).

For example:

cd ~
mkdir .emacs.d
git clone https://github.com/bbatsov/prelude.git
ln -s prelude .emacs.d

You can also clone my fork containing all changes and settings I will describe in this post:

cd ~
mkdir .emacs.d
git clone https://github.com/szw/prelude.git
cd prelude
git checkout develop
cd ..
ln -s prelude .emacs.d

Configuration

Modules

Prelude consists of modules you can turn on/off depending on your needs. All you need to do is to copy to the main Prelude’s directory and edit sample/prelude-modules.el. In my configuration I’ve turned on:

  • prelude-ido
  • prelude-company
  • prelude-programming
  • prelude-c
  • prelude-clojure
  • prelude-coffee
  • prelude-css
  • prelude-emacs-lisp
  • prelude-go
  • prelude-js
  • prelude-lisp
  • prelude-org
  • prelude-python
  • prelude-ruby
  • prelude-scala
  • prelude-shell
  • prelude-scss
  • prelude-web
  • prelude-xml
  • prelude-yaml

In the end, you just need to comment/uncomment individual modules, and it’s done.

Pinned Packages

Another great feature of Prelude is the ability to use stable repositories for Melpa packages. It’s super important in the case of Ensime, as the unstable version contains experimental features and quite often does not work properly.

To use stable repositories copy to the main Prelude’s directory and edit sample/prelude-pinned-packages.el. In my repo, I removed stable versions of Go-related packages as they caused some compatibility issues.

Preloaded Settings

In Prelude personal settings sit in a directory personal and personal/preload. The preload folder can be used to put configuration files that should be executed at the very beginning of Prelude startup. Both directories are empty for now. Let’s add a file config.el in personal/preload directory with the following content:

(modify-all-frames-parameters '((font . "SF Mono-12")
                                (vertical-scroll-bars . nil)
                                (horizontal-scroll-bars . nil)
                                (tool-bar-lines 0)
                                (menu-bar-lines 0)
                                (width . 160)
                                (height . 50)))

(setq ad-redefinition-action 'accept)
(setq exec-path-from-shell-check-startup-files nil)

The code above would set a font (make sure you would set your favorite one), remove ugly scroll bars, menu bars, and provide default width and height. Don’t hesitate to adjust it according to your needs. It’s worth to notice those settings are defaults for all new frames you would create. A frame in Emacs is more or less a new window of the same app behaving like a tab. We will touch frames soon.

ad-redefinition-action and exec-path-from-shell-check-startup-files would help a little bit with unwanted messages during and after startup.

If you like your Emacs to be always centered on the screen while starting add there also set-frame-position function call:

(set-frame-position (selected-frame)
                    (/ (- (x-display-pixel-width) (frame-pixel-width)) 2)
                    (/ (- (x-display-pixel-height) (frame-pixel-height)) 2))

Personal Settings

Personal settings are placed in a file personal/config.el. Let’s divide settings to various sections. You can pick and paste to that file whatever you want from code snippets below listed or get the whole file from here.

General Settings

(tool-bar-mode 0)
(menu-bar-mode 0)

(setq prelude-guru nil)
(setq prelude-use-smooth-scrolling t)
(setq diff-hl-side 'right)
(setq flx-ido-threshold 1000)

Tool and menu bar are off since I would prefer to do stuff more in keyboard Emacs way. Guru mode prevents from using old habits of moving around with arrows, but I’ve found it annoying. Diff highlighting looks way better at right fringe as it wouldn’t interfere with other stuff - like errors - displayed on the left. Finally, Ido flx filtering algorithm is disabled on larger collections to improve overall performance.

In this section we set wrapping lines in text-like modes and adjust scratch buffer settings to run on startup as an empty Org file. It’s super handy when it comes to making quick TODO lists :).

(setq initial-major-mode 'org-mode)
(setq initial-scratch-message nil)
(add-hook 'text-mode-hook #'toggle-word-wrap)

Custom Packages

(prelude-require-packages '(ido-vertical-mode
                            ido-ubiquitous
                            cql-mode
                            iedit
                            ag
                            meghanada
                            golden-ratio))

(setq prelude-whitespace nil)
(setq prelude-auto-save nil)
(setq ido-everywhere t)
(ido-vertical-mode 1)
(setq ido-vertical-define-keys 'C-n-and-C-p-only)
(setq ido-default-buffer-method 'selected-window)
(setq ido-default-file-method 'selected-window)
(setq aw-scope 'frame)

(add-hook 'before-save-hook 'delete-trailing-whitespace)

(global-git-commit-mode t)
(setq create-lockfiles nil)
(setq dired-use-ls-dired nil)
(setq ns-pop-up-frames nil)

prelude-require-packages function is the preferred way to install custom packages in Prelude. Ido enhancements set ido where possible and provide a vertical version of it. CQL mode is something useful when you play a lot with Cassandra files. Ag is useful when searching a whole project (with projectile). IEdit and GoldenRatio I will cover soon whereas Meghanada is a good support for Java mode.

The rest of entries are just some initial settings for those custom packages. You can read more about particular options on relevant package sites or use Emacs help, e.g. C-h v.

Custom Key Bindings

Proper key bindings are super important while dealing with Emacs. From my very first observations, there are two common mistakes regarding a correct approach to keyboard settings in Emacs.

The first one is the urge to stick to bare bones. It means no customization at all because initial settings must be the best. Well, they are not. Emacs development started 40 years ago, and standard keyboards of Lisp machines were a bit different than PC keyboards nowadays.

Symbolics LM-2 Lisp machine space-cadet keyboard

Photo by Dave Fischer (CC BY-SA 3.0)

But the vice versa approach is not that good either. If you redefine everything, you wouldn’t be compatible with the majority of plugins and tutorials anymore. Needless to say how painful it would be to maintain.

In Emacs we have following modifier keys:

Mac Emacs Shortcut
Shift Shift Capital Letter
Ctrl / Caps Lock Control C-
Alt Meta M-
Cmd Super s-

The problem with Emacs keys on a Mac keyboard is the availability of Alt (Meta) keys for your thumbs - remember we’ve already bound Ctrl to Caps Lock to comfort your left little finger. I don’t like the simple solution of just switching Command with Alt (C-c w). In the end, it seems more confusing as Super-… combinations are usually connected to OS global behaviors, like e.g. Cmd-Tab.

In my option, the reasonable solution is a path between those two approaches. In the configuration there is a set of bindings for Super/Cmd modifiers that you are can easily modify. The purpose is to make all most useful stuff - especially movements - available as Super would be a Meta.

(add-hook 'prelude-mode-hook
          (lambda ()
            (define-key prelude-mode-map (kbd "C-c C-p") 'projectile-command-map)
            (define-key prelude-mode-map (kbd "s-p") nil)
            (define-key prelude-mode-map (kbd "s-g") nil)))

(add-hook 'cider-mode-hook
          (lambda ()
            (define-key cider-mode-map (kbd "C-c C-p") nil)))

(add-hook 'ido-setup-hook
          (lambda ()
            (define-key ido-completion-map (kbd "s-n") 'ido-next-match)
            (define-key ido-completion-map (kbd "s-p") 'ido-prev-match)
            (define-key ido-completion-map (kbd "s-g") 'minibuffer-keyboard-quit)))

(add-hook 'isearch-mode-hook
          (lambda ()
            (define-key isearch-mode-map (kbd "s-g") 'isearch-abort)))

(global-set-key (kbd "s-ยง") 'crux-switch-to-previous-buffer)
(global-set-key (kbd "s-1") 'ace-window)
(global-set-key (kbd "s-b") 'backward-word)
(global-set-key (kbd "s-f") 'forward-word)
(global-set-key (kbd "<C-s-268632066>") 'sp-backward-sexp) ;;C-s-b
(global-set-key (kbd "<C-s-268632070>") 'sp-forward-sexp) ;;C-s-f
(global-set-key (kbd "<C-s-268632085>") 'sp-backward-up-sexp) ;;C-s-u
(global-set-key (kbd "s-a") 'backward-sentence)
(global-set-key (kbd "s-e") 'forward-sentence)
(global-set-key (kbd "s-{") 'backward-paragraph)
(global-set-key (kbd "s-}") 'forward-paragraph)
(global-set-key (kbd "s-<") 'beginning-of-buffer)
(global-set-key (kbd "s->") 'end-of-buffer)
(global-set-key (kbd "s-d") 'kill-word)
(global-set-key (kbd "<s-backspace>") 'backward-kill-word)
(global-set-key (kbd "s-w") 'easy-kill)
(global-set-key (kbd "s-x") 'smex)
(global-set-key (kbd "s-_") 'undo-tree-redo)
(global-set-key (kbd "s-z") 'zop-up-to-char)
(global-set-key (kbd "s-q") 'fill-paragraph)
(global-set-key (kbd "s-s") 'sp-splice-sexp)
(global-set-key (kbd "s-n") 'next-line)
(global-set-key (kbd "s-p") 'previous-line)
(global-set-key (kbd "s-g") 'keyboard-quit)
(global-set-key (kbd "C-o") 'crux-smart-open-line)
(global-set-key (kbd "C-*") 'iedit-mode)

(define-key key-translation-map (kbd "C-S-s-f") (kbd "C-M-S-f"))
(define-key key-translation-map (kbd "C-S-s-b") (kbd "C-M-S-b"))

s-n, s-p, and s-g act as universal next / previous / quit commands. I will explain it soon. Two next keybindings are handy switch buffer / move to window commands (assuming English International Mac keyboard layout). Then we have a set of movements. Finally, some Prelude goodies from various packages. Most of those bindings would work with Meta in the same way.

We have removed a default Projectile binding s-p to the Projectile command map. The same binding is also available via C-p p. It is still super handy but very often I had issues while typing fast C-p p f or C-p p p and instead got C-p C-p f. To avoid such problems with releasing Control key too fast, we have introduced additional binding - C-p C-p - doing exactly what C-p p does.

s-n, s-p, s-g are also used in Company and Popup modes:

(with-eval-after-load 'company
  (define-key company-active-map (kbd "s-g") #'company-abort)
  (define-key company-active-map (kbd "s-n") #'company-select-next-or-abort)
  (define-key company-active-map (kbd "s-p") #'company-select-previous-or-abort))

(with-eval-after-load 'popup
  (define-key popup-menu-keymap (kbd "s-g") #'keyboard-quit)
  (define-key popup-menu-keymap (kbd "s-n") #'popup-next)
  (define-key popup-menu-keymap (kbd "s-p") #'popup-previous))

Company Mode is utilized in text completion. Popup Mode is used for some Ensime features. The bindings listed here set s-n, s-p as universal movements and I’ve found it very convenient. You can move up/down either with C-p and C-n, or s-p / s-n. However in the Company Mode C-p, C-n would close completion window and move cursor line up/down. Contrary, M-p / M-n would rather change the selected word. Therefore s-p, s-n are like a conjunction of those two approaches - similarly to arrows.

Besides that, it’s also super comfortable for your fingers (thumbs) to be able to use left and right Super (Cmd) keys in combination with p, n, and g commands.

IEdit, Golden Ratio, Meghanada

Two last keybindings deserve a separate section. IEdit is a tool acting as a multiple cursors feature from Sublime Text.

(global-set-key (kbd "C-*") 'iedit-mode)

To limit editing to the current line only use M-I. Then M-{ and M-} will expand the search region one line up and down. Use so-called prefix argument (C-u) to shrink: C-u M-{ and C-u M-}.

golden-ratio is a handy plugin automatically resizing windows to the golden ratio proportions. I’ve created a small function for easy toggling and bound it to s-\:

(add-hook 'golden-ratio-mode-hook
          (lambda ()
            (if golden-ratio-mode
                (golden-ratio)
              (balance-windows))))
(global-set-key (kbd "s-\\") 'golden-ratio-mode)

(defadvice ace-window
    (after golden-ratio-resize-window)
  (golden-ratio) nil)

Finally, Meghanada is an Ensime-like plugin for Maven-based Java projects. It provides code completion, basic refactoring, and a proper Java syntax support. Here’s some initial configuration:

(require 'meghanada)
(add-hook 'java-mode-hook (lambda () (meghanada-mode t)))

Bug Workarounds

Emacs 25 is a relatively new release but Prelude seems to support it quite well. After upgrading, I had an issue with Flyspell language detection. A necessary workaround is to set LANG environment variable directly in Emacs config file. Add this to the personal/config.el file if you see Flyspell errors in the *Messages* buffer:

(setenv "LANG" "en_US.UTF-8")

Another very annoying bug I watch in Emacs in macOS is the crash during closing frames previously maximized. To verify you can play around C-x 5 2 - to create a new frame, maximize it, and the try C-x 5 0 (close current) or C-x 5 1 (close others). There are some timing and threading issues, so a workaround is to demaximize promptly before frame deletion.

(defun demaximize-frame (orig-fun &rest args)
  (if (equal (assoc 'fullscreen (frame-parameters))
             (cons 'fullscreen 'fullboth))
      (progn (toggle-frame-fullscreen)
             (sleep-for 2)
             (apply orig-fun args)
             (modify-frame-parameters nil '((fullscreen . fullboth))))
    (apply orig-fun args)))

(advice-add 'delete-frame :around #'demaximize-frame)

The last improvement in this section is a fixed behavior of Smartparens in the Clojure mode. In Clojure to achieve so-called syntax quoting you have to put a single backtick ` before a form. So we usually don’t need any Smartparens actions for backticks:

(sp-local-pair 'clojure-mode "`" nil :actions nil)
(sp-local-pair 'cider-repl-mode "`" nil :actions nil)

Ensime

The last but not least part is Ensime configuration. Firstly, there is another workaround for a bug related to mouse tracking. The workaround repairs type information hints displaying on mouse hovering in Ensime.

(require 'ensime)

(defun set-mouse-tracking ()
  (interactive)
  (set (make-local-variable 'track-mouse) t))

(eval-after-load 'ensime '(define-key ensime-mode-map [mouse-1] 'set-mouse-tracking))

Then we have some settings to prevent additional warnings and disable auto connecting.

(setq exec-path (append exec-path '("/usr/local/bin")))
(setq ensime-auto-connect 'never) ;; always, ask, never
(setq ensime-startup-snapshot-notification nil)

Finally, Ensime has a bug feature called semantic highlighting. It’s experimental, so it doesn’t work quite well. If you want to disable highlighting of implicits you can use following settings:

(setq ensime-sem-high-faces
      '((var . scala-font-lock:var-face)
        (val :inherit font-lock-constant-face :slant italic)
        (varField . scala-font-lock:var-face)
        (valField :inherit font-lock-constant-face :slant italic)
        (functionCall . font-lock-function-name-face)
        (operator . font-lock-keyword-face)
        (param :slant italic)
        (class . font-lock-type-face)
        (trait :inherit font-lock-type-face :slant italic)
        (object . font-lock-constant-face)
        (package . font-lock-preprocessor-face)
        (implicitConversion . nil)
        (implicitParams . nil)
        (deprecated :strike-through "dark gray")))

But I don’t like it at all - it loses tracking at a more complex code. To turn off semantic highlighting use:

(setq ensime-sem-high-enabled-p nil)

What’s Next?

That’s all configuration. You can run Emacs, and it will start installing packages automatically. Ensime would require more stuff to download for the first time too - when you open an SBT project.

Once the installation is done, you should restart Emacs. Close it with C-x C-c and run again. At that point, it should be ready, and you can enjoy your beautifully configured Emacs. To learn more about how to use the thing check Mastering Emacs by Mickey Petersen. And of course - among other significant resources on the Internet - read my blog! I’m going to continue with covering Emacs basics, usage, tips & tricks, and my overall experiences. I hope it would be a good starting point in the future.

If you enjoy this post feel free to leave a comment or share :).


Back to posts


comments powered by Disqus