Johan Bolmsjö's GNU Emacs Configuration

Installation instruction:

git clone github.com/johan-bolmsjo/emacs.d ~/.emacs.d

Table of Contents

1. The Init File

Requires GNU Emacs version 28.1 or greater.

The main init file is init.el. This file loads packages that require external programs to function properly. See chapter 3 for how to install required programs for specific packages.

1.1. Overview

Overview of enabled packages.

1.1.1. Package Management

Emacs packages are installed using the straight package manager. The advantage with this package manager is that it keeps the complete upstream Git repositories available together with lock files to pin down exact versions. This means that the Emacs configuration is reproducible. It's also possible to easily change package versions to resolve incompatibilities when the configuration is copied between hosts with different Emacs versions.

The version lock file ~/.emacs.d/straight/versions/default.el is versioned by this Git repository.

The actual upstream package source code is not managed by this Git repository. They are downloaded by Emacs when started. For local backup purposes it's possible to just archive the whole ~/.emacs.d directory. It's transferable between machines.

1.1.2. Documentation Modes

1.1.3. Data Formats

1.1.4. Programming Languages Modes

All programming language modes except elisp use eglot to provide code navigation and completion support. Some configuration may be missing for SLIME as I've barely used it.

1.1.5. Miscellaneous Support Functions

1.2. Keybindings

Keybindings that are specific to this configuration file.

1.2.1. User Keybindings

See https://www.gnu.org/software/emacs/manual/html_node/elisp/Key-Binding-Conventions.html for user keybinding conventions. Also http://xahlee.info/emacs/emacs/keyboard_shortcuts.html for practical overrides.

C-c a
align-current
C-c c
compile
C-c g
find-grep
C-c i
ispell-region
C-c o
ff-find-other-file (in c-mode-common)
C-c s
sort-lines
C-c t
toggle treemacs pane
Incremental Search
C-c k
Set mark at start of current match and exit isearch (user function).
Navigation

Even though these functions are called error, they work in many contexts. E.g. moving through compilation errors, grep results, xref references.

F9
next-error
F10
prev-error (overrides global menu popup)
Window Management

Commands useful to prevent Emacs from splitting frames and replacing buffers in windows when doing certain tasks.

C-c F
Lock current frame for automatic splitting.
C-c f
Unlock current frame for automatic splitting.
C-c W
Lock current window to its current buffer.
C-c w
Unlock current window from its current buffer.

See https://smythp.com/emacs_buffers/ for an explanation of the frames, windows and buffers terminology.

Denote
C-c n
Command prefix

The main note taking command is C-c n n. Other keybindings are listed when entering the command prefix. Discover all commands by entering M-x denote

Org Mode
C-c p
org-agenda (planning)
C-c L
org-store-link (works in other modes to store links)
Eglot Mode

Note that eglot relies on the keybindings of other packages for most functions. E.g. xref for navigation and eldoc for documentation.

C-c e a
eglot-code-actions
C-c e f
eglot-format (ask server to format buffer or the active region)
C-c e r
eglot-rename (rename symbol at point)
C-c e i
eglot-find-implementation
C-c e t
eglot-find-typeDefinition
Flymake Mode

Aligned with flycheck counterparts.

C-c ! l
flymake-show-buffer-diagnostics
C-c ! n
flymake-goto-next-error
C-c ! p
flymake-goto-prev-error
Embark

Embark executes context aware actions. For example opening the URL that the cursor is placed on in a buffer. It extends some of the consult commands by making it possible to export the current selection to regular buffers for easier manipulation.

C-.
Execute action
C-;
"Do what I mean"
C-h B
embark-bindings; alternative for `describe-bindings'

Note that C-. may be hijacked as a system shortcut to insert a Unicode code point or Emoji. Disable it in ibus-settings on Linux systems.

GDB (debugger)
F5
Continue program being debugged.
F6
Step till next source line, do not enter subroutine (next).
F7
Step till next source line, enter subroutine (step).
F8
Execute until current stack frame returns.
S-F8
Stop debugging

1.2.2. Overridden Keybindings

Modifies standard Emacs behavior or use non-user prefix.

F10
prev-error (overrides global menu popup)
M-n
scroll-up-command
M-p
scroll-down-command
M-f
forward-symbol (replaces forward-word)
M-b
my/backward-symbol (replaces backward-word)
M-C-f
forward-paragraph (replaces forward-sexp)
M-C-b
backward-paragraph (replaces backward-sexp)
C-x k
my/fast-kill-buffer (replaces kill-buffer)

1.3. User Facing Functions

Convenience functions defined by the configuration.

reload-file
Calls revert-buffer without asking for confirmation

1.4. Configuration Adaptations

Configuration adaptations that may be desired when applying this Emacs configuration in a new environment.

1.4.1. Terminal Emulator

The solarized theme that is used by this Emacs configuration only works properly in graphical mode and terminals that support true color.

The website https://github.com/termstandard/colors describes how to configure terminals and associated tools to support true color.

In essence, the environment variable COLORTERM must be set to truecolor. COLORTERM is distinct from the usual TERM environment variable that communicate terminal capabilities. Obviously the underlying terminal must also support the true color escape codes.

My ~/.bashrc contains the following lines to set it for the suckless terminal:

# Set the COLORTERM variable to "truecolor" if the terminal supports it.
# The suckless terminal (st) definitely does.
# The tmux-256color can be any underlying type so is technically incorrect;
# it solves the issue of SSH to remote system from within a Tmux session.
if [ "$TERM" = st-256color ] || [ "$TERM" = tmux-256color ]; then
   export COLORTERM=truecolor
fi

1.4.2. Shell Environment   optional

You may want to source ~/.emacs.d/etc/bashrc from your ~/.bashrc file. Read the small script to find its purpose.

if [ -f ~/.emacs.d/etc/bashrc ]; then
    . ~/.emacs.d/etc/bashrc
fi

You may also want to source ~/.emacs.d/etc/profile from your ~/.profile or ~/.bash_profile to add ~/.emacs.d/bin to the program search path.

if [ -f ~/.emacs.d/etc/profile ]; then
    . ~/.emacs.d/etc/profile
fi

1.4.3. Fonts   optional

A personal choice, my current favorite monospaced fonts can be found in the ~/.emacs.d/fonts/gofont directory.

Installation (Linux):

mkdir -p ~/.fonts
cp ~/.emacs.d/fonts/gofont/*.ttf ~/.fonts

Update ~/.emacs.d/fonts.el with your preferred fonts and default size.

1.4.4. Indexed Grep

I've opted to replace the grep-find command with a wrapper script that invokes a grep program based on an index. This speeds up grep operations in large code bases massively but it may not be to your liking. Just delete the entire section "Indexed grep search tool" from ~/.emacs.d/init.el to restore the original behavior.

See section 3.1 for details.

1.4.5. Custom Variables

Variables that may need customization (such as file paths) are stored in ~/.emacs.d/custom.el.

2. Quick Guides

Scattered quick guides for my own memory. It could do with more information for Emacs neophytes.

2.1. Navigation

Xref is used by many Emacs modes for navigation, including Eglot for navigating source code.

M-,
Go back
M-.
Find thing
M-?
Find references

Jump to a specific line.

M-g g
goto-line

2.2. Incremental Search

Don't forget about the occur mode when doing incremental search. It's very useful to get a navigable outline of all current matches.

All keybindings can be listed by invoking C-h b when in search mode.

Starting search:

C-s
Search forward for a literal string
C-r
Search backward for a literal string
C-M-s
Search forward for a regexp
C-M-r
Search backward for a regexp
M-s _
Search forward for a symbol
M-s .
Search forward for the symbol at point

During search:

C-s
Move to next match (C-s C-s to resume search)
C-r
Move to previous match (C-r C-r to resume search)
C-g
Abort search
M-c
Toggle case sensitive search
M-e
Edit search term
M-s o
Outline of current matches (occur)
M-s SPC
Toggle lax whitespace
RET
Terminate search, leaving cursor at the current match

2.3. Completion in Buffers

Completion is provided by Corfu together with Orderless. Completion is triggered by C-M-i which is bound to complete-symbol. The TAB key is also configured to either indent (if it can), else complete. This does not work in the C/C++ mode.

The completion mechanism provided by orderless is a bit different and can take some time to get used to. Multiple patterns (space separated words) can be entered. Completions candidates that match all patterns regardless of order are kept. Patterns can be regexps as well as regular words, e.g. ^desc match candidates starting with desc.

The built-in dabbrev mode can also be useful.

M-/
dabbrev-expand
C-M-/
dabbrev-completion

2.4. Minibuffer Completion

Vertico together with Marginalia and Orderless is used to enhance minibuffer completions. For example selecting buffers, files or the function to invoke via M-x.

TAB
Navigate to selected candidate
RET
Accept selected candidate
M-RET
Submit exactly what is typed (ignore candidate completion)
C-g
Abort (as always)

Searching for previous selections is done using C-s and C-r. Navigation is performed using the usual keybindings.

2.5. Magit

Magit is a complete text-based user interface to Git. The magit status command is bound to C-x g.

2.6. Denote

The YouTube presentation https://www.youtube.com/watch?v=mLzFJcLpDFI by the package author gives a good overview of the note taking workflow. I recommend studying the manual for further details.

2.7. Org Mode

Org mode is a documentation and planning major mode. Some HOWTO notes are kept in docs/howto-org-mode.html.

The org-tempo package is enabled which provides some template instantiation shortcuts. Invoke M-x describe-variable and enter org-tempo-tags to see all shortcuts. Useful shortcuts include <s TAB for source blocks and <q TAB for quote blocks.

2.8. Text Templates

Text template support is provided by the yasnippet package. Sippets are kept under
~/.emacs.d/<MODE>/<FILE> and they are are expanded by typing their name and pressing TAB.

2.9. Diagnostics

Diagnostics such as compiler errors are provied by either the flycheck or flymake package.

2.10. Language Server Protocol Support

Eglot works in concert with project.el to identify projects. Only version controlled directories can become projects. Eglot can only analyze files that belongs to a project.

Invoke M-x p p to add a project, select the … (choose a dir) option. Tracked projects are stored in ~/.emacs.d/projects.

Eglot must be started manually from an opened file that belongs to a project. This is done by invoking M-x eglot. Eglot may ask for the language server to use if it can't find one or there are multiple choices. After having done this once, eglot does not prompt for other files in the same project.

Eglot is well integrated with core Emacs packages. Apart from the mentioned xref it use the eldoc package to display documentation and type information. Invoke C-h . to show documentation at point.

2.11. Keyboard Macros

Keyboard macros are incredibly useful to apply repetitive changes in close proximity. Press F3 to start recording key presses. Press F4 to end the recording and bind it to the same key.

2.12. Debugging Using GDB

Activate by M-x gdb followed by M-x gdb-many-windows. The toolbar contain debugger navigation icons so you may want to enable it using M-x tool-bar-mode. Keybindings are listed in section GDB (debugger).

3. Setup of External Tools

Unfortunately the Emacs init file is not self contained. External tools are required to support many packages.

3.1. Indexed Grep

The tool codesearch provides fast, indexed regexp search over large file trees.

Install the following commands (requires Go toolchain).

go install github.com/johan-bolmsjo/codesearch/cmd/{cindex,csearch}@latest

Note that the only integration is that the grep-find command has been changed to invoke ~/.emacs.d/bin/csearch-color instead.

The convenience scripts in ~/.emacs.d/bin/ has the following purpose:

cindex-append
Scan directories for source files to add to code index.
cindex-reset
Clear code index.
csearch-color
Colorize grep matches for Emacs.

3.2. Shell Script Mode

Install the shellcheck linter to get good advice on shell script constructs.

Ubuntu/Debian specific instruction:

apt install shellcheck

3.3. PlantUML Documentation

Install PlantUML.

Ubuntu/Debian specific instruction:

apt install plantuml

3.4. Go Programming Language Mode

Install the Go programming language toolchain as instructed on https://go.dev/dl/

Install required tools:

go install golang.org/x/tools/gopls@latest
go install golang.org/x/tools/cmd/goimports@latest
go install golang.org/x/tools/cmd/gorename@latest
go install github.com/rogpeppe/godef@latest

3.5. OCaml Programming Language Mode

Install the OCaml language toolchain from https://ocaml.org/releases/

Install required tools:

opam update
opam switch create 4.13.1
opam install dune utop ocaml-lsp-server merlin tuareg ocp-indent odig

The OCaml setup is a bit special in that the Emacs packages are installed by the steps above. Not by the Emacs package manager.

3.6. Python Programming Language Mode

3.6.1. Python Virtual Environments

Pyenv is used to compartmentalize python installations and make it possible to switch between them for different projects.

Install pyenv from https://github.com/pyenv/pyenv:

Clone the pyenv repo:

git clone https://github.com/pyenv/pyenv.git ~/.pyenv
cd ~/.pyenv && src/configure && make -C src

Put the following in ~/.bashrc:

export PYENV_ROOT="$HOME/.pyenv"
if [ -d "$PYENV_ROOT" ]; then
    command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
    eval "$(pyenv init -)"
fi

Apply the changes in the current shell (or login again):

exec "$SHELL"

Install Python build dependencies (Ubuntu specific, refer to https://github.com/pyenv/pyenv/wiki#suggested-build-environment for other OSes):

sudo apt-get update
sudo apt-get install make build-essential libssl-dev zlib1g-dev libbz2-dev \
     libreadline-dev libsqlite3-dev wget curl llvm libncursesw5-dev xz-utils \
     tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev

Install a Python version using pyenv:

pyenv install 3.10.7

Switching between Python versions:

pyenv shell VERSION
Select just for current shell session.
pyenv local VERSION
Automatically select whenever you are in the current directory (or its subdirectories).
pyenv global VERSION
Select globally for your user account.

3.6.2. Language Server

Install the language server:

pip install 'python-language-server[all]' scrapy

3.7. Zig Programming Language Mode

4. Compiling GNU Emacs

Compiling GNU Emacs from source is quite easy. Many packages see heavy development and may require a recent version.

4.1. Emacs 28.2

Install dependencies (Ubuntu 22.04 specific):

sudo apt install \
     libcairo2-dev \
     libgccjit-11-dev \
     libgif-dev \
     libgnutls28-dev \
     libharfbuzz-dev \
     libjansson-dev \
     libjpeg-dev \
     libncurses-dev \
     libtiff-dev \
     libxaw7-dev \
     libxml2-dev \
     libxpm-dev

Download, configure, build and install GNU Emacs on Linux:

wget https://ftp.acc.umu.se/mirror/gnu.org/gnu/emacs/emacs-28.2.tar.xz
tar xf emacs-28.2.tar.xz
cd emacs-28.2
./configure \
    --with-native-compilation \
    --with-mailutils \
    --with-x-toolkit=lucid \
    --prefix=$HOME/.local
make -j16
make install

Make sure that Cairo is used for the GUI in the configure stage or some library dependency is missing.

4.2. Emacs 29.1

Install dependencies (Ubuntu 22.04 specific):

sudo apt install \
   libcairo2-dev \
   libgccjit-11-dev \
   libgif-dev \
   libgnutls28-dev \
   libharfbuzz-dev \
   libjansson-dev \
   libjpeg-dev \
   libncurses-dev \
   librsvg2-dev \
   libsqlite3-dev \
   libtiff-dev \
   libtree-sitter-dev \
   libxaw7-dev \
   libxml2-dev \
   libxpm-dev

Download, configure, build and install GNU Emacs on Linux:

wget https://ftp.acc.umu.se/mirror/gnu.org/gnu/emacs/emacs-29.1.tar.xz
tar xf emacs-29.1.tar.xz
cd emacs-29.1
./configure \
    --with-native-compilation \
    --with-mailutils \
    --with-x-toolkit=lucid \
    --with-tree-sitter \
    --prefix=$HOME/.local
make -j16
make install

Make sure that Cairo is used for the GUI in the configure stage or some library dependency is missing.

Install tree-sitter language grammar files using M-x treesit-install-language-grammar; example language names: python, c, cpp.

See https://www.masteringemacs.org/article/how-to-get-started-tree-sitter for an introduction to Emacs tree-sitter support and how to use it.

For now, none of the tree-sitter major modes are enabled by default.

Date: 2024-10-05