Configuring Emacs for Swift Development

Emacs is a highly customizable text editor that has its origins in a macro package for the Digital Equipment Corporation TECO editor. A number of Emacs-like editors and derivatives have existed over the years, but this guide will focus on GNU Emacs.

This guide will walk you through setting up a new install of Emacs for Swift development; it does not assume any particular operating system, but where relevant may give system-specific hints to help you get started. It also is not intended to teach you to use Emacs; if you want to learn Emacs, try this resource: Emacs Tour.

We assume for the purposes of this guide that you have already installed Swift; if not, please follow the installation instructions for your platform on the Swift website before continuing.

Installing Emacs

There are generic installation instructions on the GNU Emacs website. Specific instructions are given below for some common platforms.

macOS

The Emacs for Mac OS X website provides universal binaries in a standard Mac disk image. Downloading the image from that site, opening it and dragging it to your Applications folder is the easiest way to install a native Emacs for macOS development.

Microsoft Windows

You can download GNU Emacs for Windows from a nearby GNU mirror or the main GNU FTP server. The simplest thing is likely to run the emacs-<version>-installer.exe executable, which will install Emacs and set up some shortcuts for you.

Debian-based Linux (including Ubuntu)

From a prompt, enter

$ sudo apt-get install emacs

or if you are doing this on a server system or in a container where you have no plans to use the GUI,

$ sudo apt-get install emacs-nox

RedHat-based Linux (RHEL, Centos, Fedora)

From a prompt, enter

$ sudo dnf install emacs

(On older builds you may need to use yum rather than dnf, but the latter is the newer, better option if it’s available.)

Configuring Emacs

Emacs veterans probably remember downloading and installing Lisp packages manually, but these days it’s best to use a package manager. We’ll also configure MELPA, which is a popular package repository for Emacs. To do this, open Emacs, enter C-x C-f ~/.emacs and hit enter. Then add the following to your .emacs file:

;;; Add MELPA as a package source
(require 'package)
(setq package-enable-at-startup nil)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(package-initialize)

We’ll also set-up use-package, since that simplifies package installation if you want to be able to just copy your ~/.emacs to a new machine and have things just work. To do that, add

;;; Bootstrap `use-package'
(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))

(eval-when-compile
  (require 'use-package))

In a moment we’ll want to be able to locate sourcekit-lsp; this depends on where Swift is installed, so let’s add a Lisp function to do that:

;;; Locate sourcekit-lsp
(defun find-sourcekit-lsp ()
  (or (executable-find "sourcekit-lsp")
      (and (eq system-type 'darwin)
           (string-trim (shell-command-to-string "xcrun -f sourcekit-lsp")))
      "/usr/local/swift/usr/bin/sourcekit-lsp"))

Next, we’ll install some useful packages:

;;; Packages we want installed for Swift development

;; .editorconfig file support
(use-package editorconfig
    :ensure t
    :config (editorconfig-mode +1))

;; Swift editing support
(use-package swift-mode
    :ensure t
    :mode "\\.swift\\'"
    :interpreter "swift")

;; Rainbow delimiters makes nested delimiters easier to understand
(use-package rainbow-delimiters
    :ensure t
    :hook ((prog-mode . rainbow-delimiters-mode)))

;; Company mode (completion)
(use-package company
    :ensure t
    :config
    (global-company-mode +1))

;; Used to interface with swift-lsp.
(use-package lsp-mode
    :ensure t
    :commands lsp
    :hook ((swift-mode . lsp)))

;; lsp-mode's UI modules
(use-package lsp-ui
    :ensure t)

;; sourcekit-lsp support
(use-package lsp-sourcekit
    :ensure t
    :after lsp-mode
    :custom
    (lsp-sourcekit-executable (find-sourcekit-lsp) "Find sourcekit-lsp"))

We’ll also add a couple of packages to make things look prettier and more modern; you could install a theme or change the fonts as well if you wish - the possibilities for customizing Emacs are nearly endless:

;; Powerline
(use-package powerline
  :ensure t
  :config
  (powerline-default-theme))

;; Spaceline
(use-package spaceline
  :ensure t
  :after powerline
  :config
  (spaceline-emacs-theme))

Finally, let’s turn off the splash screen, since we don’t want to see that whenever we start Emacs, and we’ll also disable the toolbar:

;;; Don't display the start screen
(setq inhibit-startup-screen t)

;;; Disable the toolbar
(tool-bar-mode -1)

Enter C-x C-s to save your new .emacs file, then restart Emacs.

Conclusion

We were able to get Emacs set-up to edit Swift code, with syntax highlighting and integration with SourceKit-LSP as well as support for modern best practices like the use of .editorconfig files to allow projects to easily set their own preferences for tab widths and formatting.

Files

Here’s a complete .emacs file containing the full configuration:

;;; Add MELPA as a package source
(require 'package)
(setq package-enable-at-startup nil)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(package-initialize)

;;; Bootstrap `use-package'
(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))

(eval-when-compile
  (require 'use-package))

;;; Locate sourcekit-lsp
(defun find-sourcekit-lsp ()
  (or (executable-find "sourcekit-lsp")
      (and (eq system-type 'darwin)
           (string-trim (shell-command-to-string "xcrun -f sourcekit-lsp")))
      "/usr/local/swift/usr/bin/sourcekit-lsp"))

;;; Packages we want installed for Swift development

;; .editorconfig file support
(use-package editorconfig
    :ensure t
    :config (editorconfig-mode +1))

;; Swift editing support
(use-package swift-mode
    :ensure t
    :mode "\\.swift\\'"
    :interpreter "swift")

;; Rainbow delimiters makes nested delimiters easier to understand
(use-package rainbow-delimiters
    :ensure t
    :hook ((prog-mode . rainbow-delimiters-mode)))

;; Company mode (completion)
(use-package company
    :ensure t
    :config
    (global-company-mode +1))

;; Used to interface with swift-lsp.
(use-package lsp-mode
    :ensure t
    :commands lsp
    :hook ((swift-mode . lsp)))

;; lsp-mode's UI modules
(use-package lsp-ui
    :ensure t)

;; sourcekit-lsp support
(use-package lsp-sourcekit
    :ensure t
    :after lsp-mode
    :custom
    (lsp-sourcekit-executable (find-sourcekit-lsp) "Find sourcekit-lsp"))

;; Powerline
(use-package powerline
  :ensure t
  :config
  (powerline-default-theme))

;; Spaceline
(use-package spaceline
  :ensure t
  :after powerline
  :config
  (spaceline-emacs-theme))

;;; Don't display the start screen
(setq inhibit-startup-screen t)

;;; Disable the toolbar
(tool-bar-mode -1)