This document contains configuration for the text editor Emacs. It’s written
as a literate elisp program using org-mode
. To generate the actual
configuration files used by Emacs you need to “tangle” (org-babel-tangle
,
typically bound to C-c C-v t
).
We start with some boilerplate commentary according to Emacs conventions:
;;; init.el --- emacs config -*- lexical-binding: t -*-
;; Copyright (C) 2024 Lewis Weinberger
;; Author: Lewis Weinberger
;; URL: https://github.com/lewis-weinberger/dotemacs
;;; Commentary:
;; My Emacs configuration
;; This file is auto-generated!
;;; Code:
Next we define some default configuration settings:
;; General configuration -------------------------------------------------------
(setq-default inhibit-startup-screen t ;; don't display startup
mouse-autoselect-window t ;; window focus follows mouse
frame-resize-pixelwise t ;; window manager size hints
frame-inhibit-implied-resize t ;; prevent unneeded resizing
require-final-newline t ;; newline before EOF
sentence-end-double-space nil ;; only one space after period
indent-tabs-mode nil ;; use spaces for indentation
tab-width 8 ;; display width of tab character
delete-selection-mode t ;; overwrite selected text
confirm-kill-processes nil ;; don't check to exit emacs
use-short-answers t ;; don't require full "yes/no"
dired-kill-when-opening-new-dired-buffer t ;; one dired buffer
ring-bell-function 'ignore ;; no bells please
ido-everywhere t ;; use ido for file and buffer opening
ido-enable-flex-matching t ;; fuzzy matching in ido
cursor-type 'bar ;; show cursor tick as a bar not a box
package-native-compile t ;; ahead-of-time package compilation
use-package-always-ensure t ;; auto-install packages
custom-file "~/.emacs.d/custom.el") ;; separate customisations
Now we want to tweak which minor modes are in use. Note that the convention for mode-toggling commands is:
The toggle command takes one optional (prefix) argument. If called interactively with no argument it toggles the mode on or off. A positive prefix argument enables the mode, any other prefix argument disables it.
We need to switch off some things that we don’t want:
(menu-bar-mode -1) ;; no menu bar
(tool-bar-mode -1) ;; no tool bar
(scroll-bar-mode -1) ;; no scroll bar
And switch on other things:
(ido-mode 1) ;; interactive do
(fido-mode 1) ;; fake ido
And we want to set up some hooks and bindings for desired behaviour:
(defun lw/whitespace ()
"Keep track of whitespace: trailing, empty lines and tabs."
(setq show-trailing-whitespace t
indicate-empty-lines t
whitespace-style '(tabs tab-mark))
(whitespace-mode 1))
(defun lw/prog-mode ()
"Settings for comfortable programming."
(lw/whitespace) ;; show whitespace
(column-number-mode 1) ;; column in modeline
(subword-mode 1) ;; CamelCase word splitting
(flymake-mode 1) ;; syntax checking
(hl-line-mode 1) ;; highlight current line
(display-line-numbers-mode 1)) ;; line numbers
(add-hook 'prog-mode-hook #'lw/prog-mode)
(global-set-key (kbd "C-x C-b") 'ibuffer)
The last basic configuration tweak is to set a nice font:
(add-to-list 'default-frame-alist
'(font . "unifont:pixelsize=16:antialias=true"))
For the rest of our configuration we rely on third-party packages pulled
in using package.el
via use-package
. As of Emacs version 29 this
comes built-in with Emacs and can pull packages from both (GNU) Elpa
and
Non-GNU Elpa
, but we need to add Melpa
ourselves:
;; Third-party package configuration -------------------------------------------
;; Add Melpa as package source
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
First we start with some packages to improve the general editing experience. In
particular we use evil
for vi(m)-like modal editing:
(require 'use-package)
(defun lw/evil-insert-from-visual ()
"Enter insert mode from visual mode."
(interactive)
(when (evil-visual-state-p) (evil-exit-visual-state))
(evil-insert 1))
;; vi-keys just for programming
(use-package evil
:demand t
:custom
(evil-default-state 'emacs)
(evil-default-cursor 'bar)
:config
(define-key evil-visual-state-map "i" 'lw/evil-insert-from-visual)
(evil-set-undo-system 'undo-redo)
(evil-set-initial-state 'prog-mode 'normal)
;; the cursors need to be set after initialisation
(setq evil-normal-state-cursor 'box)
(setq evil-visual-state-cursor 'box)
(evil-mode 1))
Some further improvements to the editing experience:
;; Add ability to use more than one cursor
(use-package multiple-cursors
:bind
("C-c e" . mc/edit-lines)
("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-next-like-this)
("C-c C-<" . mc/mark-all-like-this)
("C-c v" . set-rectangular-region-anchor))
;; Matching rainbow parantheses
(use-package rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
;; Pulse highlighted line after commands
(use-package pulsar
:custom
(pulsar-pulse-functions '(scroll-up-command
scroll-down-command
recenter-top-bottom
other-window))
:config (pulsar-global-mode 1))
Next some packages to improve the general emacs experience:
;; Display key bindings to help remember key sequences
(use-package which-key
:config
(which-key-mode 1)
(which-key-setup-minibuffer))
;; Better terminal emulator implemented in elisp
(use-package eat
:hook
(eshell-load . eat-eshell-mode)
(eshell-load . eat-eshell-visual-command-mode))
The vanilla theme and layout need some tweaking:
;; Increase padding of windows and frames
(use-package spacious-padding
:custom (spacious-padding-subtle-mode-line t)
:hook (ef-themes-post-load . spacious-padding-mode))
;; εὖ themes
(use-package ef-themes
:config (ef-themes-select 'ef-night))
And finally we get into some packages to improve the programming experience in emacs:
;; Major mode for zig-lang
(use-package zig-mode
:mode ("\\.zig?\\'" . zig-mode))
;; Major mode for crystal-lang
(use-package crystal-mode
:mode ("\\.cr?\\'" . crystal-mode))
;; Linting for rust-lang
(use-package flymake-clippy
:hook (rust-ts-mode . flymake-clippy-setup-backend))
;; Linting for more languages
(use-package flymake-collection
:hook (after-init . flymake-collection-hook-setup))
;; Automatically install tree-sitter parsers and major modes
(use-package treesit-auto
:defines zig-treesit-config
:custom
(treesit-auto-install t)
:config
(setq zig-treesit-config
(make-treesit-auto-recipe
:lang 'zig
:ts-mode 'zig-ts-mode
:remap 'zig-mode
:url "https://github.com/maxxnino/tree-sitter-zig"
:ext "\\.zig\\'"))
(add-to-list 'treesit-auto-recipe-list zig-treesit-config)
(add-to-list 'treesit-auto-langs 'zig)
(treesit-auto-add-to-auto-mode-alist 'all)
(treesit-auto-install-all)
(global-treesit-auto-mode))
That’s the end of our configuration!
;;; init.el ends here