EECS 280 Tutorials

Emacs

Emacs is an extensible, customizable, free, text editor. It has an IDE mode that works with gdb and lldb. It is awdeorio’s favorite way to code. When you are good at Emacs, you can edit code very quickly.

This tutorial is intended for Emacs beginners who want to learn a powerful editor and some of its features for working with C++ projects.

Reasons to learn Emacs:

Quick start

Install.

$ brew install --cask emacs  # macOS
$ sudo apt install emacs     # Windows/WSL, Linux

Learn keyboard shortcuts with this Emacs Cheat Sheet for beginners.

Start editing files.

$ emacs main.cpp

Prerequisites

We’re going to use external command line tools. If you haven’t installed CLI tools on your machine yet, follow one of these tutorials first.

macOS Windows Linux

Make sure you have a compiler and a debugger installed. Your version might be different. Instructions for installation on macOS, Windows/WSL/Linux.

$ g++ --version  # macOS
Apple clang version 13.1.6 (clang-1316.0.21.2.5)
$ lldb --version # macOS
Apple Swift version 5.6.1 (swiftlang-5.6.0.323.66 clang-1316.0.20.12)
$ g++ --version  # WSL/Linux
g++ (GCC) 8.5.0 20210514
$ gdb --version  # WSL/Linux
GNU gdb (GDB)

Next, follow our Command line interface (CLI) tutorial.

Install

Choose your platform below.

When you’re done, you should have Emacs version 24.4 or higher.

$ emacs --version
GNU Emacs 27.1

Linux

This works for Ubuntu and other Debian-based distros.

$ sudo apt install emacs

CAEN Linux

Emacs is already installed on CAEN Linux.

macOS

Use the Homebrew package manager.

$ brew install --cask emacs

Windows/WSL

Start an Ubuntu Bash shell.

$ sudo apt install emacs

It will be easier to get GUI Emacs windows to work from inside WSL if you have Windows 11 and WSL 2.

Start and quit

Start Emacs, and you should see something like this screenshot.

$ emacs

Pro-tip: Start Emacs in the background so you can continue to use your Terminal.

$ emacs &

Quit. The Emacs notation for this keyboard shortcut is C-x C-c.

  1. Press and hold Control.
  2. Press x and release it. Continue holding Control.
  3. Press c and release it.
  4. Release Control.

Key bindings

Emacs key bindings may seem byzantine at first, but once you learn them you can edit files very quickly. When Emacs was created, keyboards looked different and today’s conventions like cut-copy-paste hadn’t been invented yet.

Without keyboard shortcuts, you can run commands by name, for example “save and quit” with M-x save-buffers-kill-terminal.

  1. Press and hold Meta (AKA Alt on Windows/Linux, AKA Option on macOS).
  2. Continue pressing Meta and press x.
  3. Release both keys.
  4. You’re now in the mini-buffer, where you can type the command save-buffer. Tab completion works in the mini-buffer.
  5. Press Return (Enter).

In Emacs documentation, M means Meta (AKA Alt on Windows/Linux, AKA Option on macOS) and C means Control.

C-x C-s: Save

  1. Press and hold Control.
  2. Press x and release it. Continue holding Control.
  3. Press s and release it.
  4. Release Control.

C-x C-c: Quit

Learn a few more keyboard shortcuts with this Emacs Cheat Sheet for beginners and keep around an Emacs Reference Card.

Pro-tip: Keep your hands on the keyboard’s home row. Avoid the mouse and arrow keys.

Warning: You might be tempted to remap Emacs keyboard shortcuts to be more familiar, M-c for copy, etc. Don’t do this! There are thousands of keybindings, and changing them can cause a chain reaction. For example, C-c is already used a prefix for many other commands.

Caps lock as control

You’ll use the control key a lot in Emacs. Some users find it more ergonomic to map the Caps Lock key to Control. Here’s the setting in MacOS.

macOS

On Apple laptops, it’s more ergonomic to map Command to Meta and Option to Super. Add this to your ~/.emacs.d/init.el.

;; macOS modifier keys
(setq mac-command-modifier 'meta) ; Command == Meta
(setq mac-option-modifier 'super) ; Option == Super

Customize

The out-of-the-box Emacs configuration is terrible. Some users start with Spacemacs or Doom Emacs, which are just Emacs shipped with a bunch of customizations. Many of these users later graduate to vanilla Emacs with their own customizations. Here’s awdeorio’s init.el.

Basic

Edit ~/.emacs.d/init.el with Emacs. Yours will probably be blank.

$ emacs ~/.emacs.d/init.el

Here are a few basic customizations that you might like. Paste with C-y.

;; Don't show a startup message
(setq inhibit-startup-message t)

;; Show line and column numbers
(setq line-number-mode t)
(setq column-number-mode t)

;; Show syntax highlighting
(global-font-lock-mode t)

;; Highlight marked regions
(setq-default transient-mark-mode t)

;; Parentheses
(electric-pair-mode 1)                  ; automatically close parentheses, etc.
(show-paren-mode t)                     ; show matching parentheses

;; Smooth scrolling (one line at a time)
(setq scroll-step 1)

;; Tab settings: 2 spaces.  See also: language-specific customizations below.
(setq-default indent-tabs-mode nil)
(setq tab-width 2)

;; Easier buffer switching
(global-set-key "\C-x\C-b" 'electric-buffer-list)

Package manager

Set up use-package, which automates package installation and configuration.

;; Configure built in package manager
(require 'package)
(package-initialize)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(add-to-list 'package-archives '("gnu" . "https://elpa.gnu.org/packages/"))

;; Install use-package
;; https://github.com/jwiegley/use-package
(when (not (package-installed-p 'use-package))
  (package-refresh-contents)
  (package-install 'use-package))
(eval-when-compile
  (require 'use-package))

Undo/redo

Emacs default undo is confusing. Undo Tree provides more intuitive undo and redo.

;; More intuitive undo/redo.  M-_ undo, C-M-_ redo
(use-package undo-tree
  :config
  (global-undo-tree-mode)
  (global-set-key "\C-\M-_" 'redo)
  :ensure t
  :defer t
  )

C/C++

Here are some optional packages that you might like for C/C++ programming.

;; Intellisense syntax checking
;; http://www.flycheck.org/en/latest/
(use-package flycheck
  :config
  ;; enable in all modes
  (global-flycheck-mode)
  ;; C++17
  (add-hook 'c++-mode-hook (lambda () (setq flycheck-clang-language-standard "c++17")))
  :ensure t
  :defer t
)

;; C and C++ programming.  Build with C-c m.  Rebuild with C-c c.  Put
;; this in c-mode-base-map because c-mode-map, c++-mode-map, and so
;; on, inherit from it.
(add-hook 'c-initialization-hook
          (lambda () (define-key c-mode-base-map (kbd "C-c m") 'compile)))
(add-hook 'c-initialization-hook
          (lambda () (define-key c-mode-base-map (kbd "C-c c") 'recompile)))
(setq-default c-basic-offset tab-width) ; indentation
(add-to-list 'auto-mode-alist '("\\.h$" . c++-mode))  ; assume C++ for .h files

Dark mode

Of course Emacs has a dark mode. Add the following to your init.el.

(use-package spacemacs-common
    :ensure spacemacs-theme
    :config (load-theme 'spacemacs-dark t)
    )

Hide menu bars

After you learn the keyboard shortcuts really well, you might not need the menu bars any more. You can remove tool bars and scroll bars like this.

;; Remove scrollbars, menu bars, and toolbars
(when (fboundp 'menu-bar-mode) (menu-bar-mode -1))
(when (fboundp 'tool-bar-mode) (tool-bar-mode -1))
(when (fboundp 'scroll-bar-mode) (scroll-bar-mode -1))

Create a project

Emacs doesn’t require any special setup for a project.

Create a new directory, then move into the new directory. Your folder location might be different.

$ mkdir ~/eecs280/p1-stats
$ cd ~/eecs280/p1-stats

Add new files

To create a new file, start Emacs with a new file name.

$ emacs main.cpp

Alternatively, create your main.cpp file from the command line using touch.

$ touch main.cpp

Copy-paste this Hello World program into your main.cpp. Remember, paste is C-y.

#include <iostream>
using namespace std;

int main() {
  cout << "Hello World!\n";
}

Add existing files

Emacs doesn’t require any special steps for adding existing files.

If you have starter files, add them to your project directory. This example is from EECS 280 Project 1, but this tutorial doesn’t require understanding the files. Your URL or files might be different.

Download, unpack, and move the starter files into the directory that already contains main.cpp. Your URL or folder might be different.

$ wget https://eecs280staff.github.io/p1-stats/starter-files.tar.gz
$ tar -xvzf starter-files.tar.gz
$ mv starter-files/* .
$ rm -rf starter-files starter-files.tar.gz

You should see your new files in your project directory.

$ tree
.
├── Makefile
├── cats.csv
├── cats.out.correct
├── main.cpp
├── p1_library.cpp
├── p1_library.hpp
├── stats.hpp
├── stats_public_tests.cpp
├── stats_tests.cpp.starter
└── two_sample.cpp.starter

Rename files

Emacs doesn’t require any special steps for renaming files.

Rename a file at the command line.

$ mv stats_tests.cpp.starter stats_tests.cpp

Compile and Run

Some Emacs users compile and run at the command line. Others compile and run inside Emacs. We’ll use the command line in this tutorial.

$ make main.exe
$ ./main.exe
Hello World!

If you don’t have a Makefile, you can compile manually.

$ g++ -g main.cpp -o main.exe

Navigate to a specific line number with M-g M-g. This is useful for jumping to a line of code reported as a compiler error.

Debug

Some Emacs users run GDB or LLDB at the command line. Others use Emacs’ GDB or LLDB integration. We’ll start at the command line.

Linux and Windows/WSL: Run GDB from the command line with the GDB Tutorial

macOS: Run LLDB from the command line with the LLDB Tutorial.

Integrated debugging

Emacs provides a user interface for GDB or LLDB. Make sure you already know how to use one.

macOS: Install and configure the realgud-lldb package by adding this to your init.el.

(use-package realgud
  :ensure t
  )
(use-package realgud-lldb
  :ensure t
  )

Compile the executable you wish to debug.

$ make main.exe

Start Emacs.

 $ emacs main.cpp

Start debug mode. Emacs runs GDB or LLDB behind the scenes.

GDB (Linux/WSL) LLDB (macOS)
M-x gud-gdb M-x realgud--lldb

Pro-tip: try M-x gdb M-x gdb-many-windows on Linux/WSL.

Set a breakpoint and run. GDB and LLDB use very similar commands. The Emacs window should split.

(lldb) b main
(lldb) r

Step into a function with s.

Move to the next line of code with n. Do this a few times until you reach this line of code.

Print a variable with p.

Quit the debugger with q. Then, close the current window pane (the debugger) with C-x 0.

Pro-tips

Tips and tricks for becoming an Emacs wizard.

Autocomplete

Use built-in Emacs autocomplete with M-/. Cycle through the completion options by holding M and continuing to hit /.

Use Company Mode to provide more complex C++ code completion by simply typing the first 3 characters of a symbol and waiting for a second. Hit TAB to cycle the completion options. M-n and M-p for next or previous completion. You’ll see the company-mode configuration in init.el.

Configure Company Mode by adding this to your init.el.

;; Autocomplete for code
;; Company docs: https://company-mode.github.io/
;; Company TNG: https://github.com/company-mode/company-mode/issues/526
(use-package company
  :config
  (company-tng-configure-default)       ; use default configuration
  (global-company-mode)
  :ensure t
  :defer t                              ; lazy loading
  )

Emacs launch shortcut

Add this to your ~/.bash_profile or ~/.zshrc to start Emacs in the background with e. Close your terminal and open it again.

e ()
{
    emacs "$@" &
}

Then you can simply type:

$ e stats.cpp

Editing remotely with TRAMP

Emacs TRAMP mode lets you edit a file on a remote server using a local GUI window.

Configure Emacs TRAMP mode to use SSH multiplexing. Add this to your ~/.emacs.d/init.el.

(use-package tramp
  :config
  (setq tramp-default-method "ssh")
  (setq tramp-ssh-controlmaster-options
        (concat
         "-o ControlMaster auto "
         "-o ControlPath ~/.ssh/socket-%%C "
         ))
  (setq tramp-use-ssh-controlmaster-options nil)
  :defer 1  ; lazy loading
)

Open an Emacs GUI window on your local machine. It doesn’t matter what directory you’re in.

$ emacs

SSH into your remote server, CAEN Linux in this example. This will set up an SSH multiplexing connection.

$ ssh login.engin.umich.edu
...

In Emacs, open the file /ssh:login.engin.umich.edu:main.cpp. Recall C-x C-f is find-file. Tab completion works in the minibuffer. You’re now editing a file main.cpp on a remote server.

Pair programming with tmux

Pair program on a remote machine with two people inside the same Emacs instance. We’ll use tmux, which is a terminal multiplexer.

Alice connects to a remote server containing her code. She starts a tmux session named shared. Then, she starts Emacs inside that tmux session.

$ ssh login.engin.umich.edu
$ hostname
caen-vnc-vm05.engin.umich.edu
$ cd p1-stats
$ tmux new -s shared
$ emacs -nw main.cpp

Bob connects to the same remote server that Alice did. He connects to Alice’s tmux session named shared. She’s already running Emacs, so he sees her Emacs session.

$ ssh caen-vnc-vm05.engin.umich.edu
$ tmux attach -t shared

Text-only Emacs

If you’re on a remote server without a GUI, you can use Emacs in text-only mode. The -nw option stands for “no window”.

$ emacs -nw

Fast text-only Emacs install

For a light weight text-only Emacs install on a Linux server, use the emacs-nox package. This is great for small tasks on remote servers.

$ sudo apt-get install emacs-nox

Nano as a fallback

Many servers have two text editors installed by default: vi and nano. Because Nano navigation keyboard shortcuts are similar to Emacs, it’s a nice alternative for environments where you need to edit some configuration files, but don’t want to install anything.

$ nano main.cpp

Troubleshooting

To reset all Emacs configuration, plugins, and settings:

$ rm -rf ~/.emacs ~/.emacs.d/

Acknowledgments

Original document written by Andrew DeOrio awdeorio@umich.edu.

This document is licensed under a Creative Commons Attribution-NonCommercial 4.0 License. You’re free to copy and share this document, but not to sell it. You may not share source code provided with this document.