15 KiB
Emacs Configuration
Shout out to Harry R. Schwartz; A whole bunch of this config (including the idea of embeddeding the lot in an Org document) is yanked from his dotfiles repo.
The rest of this config grabs packages via use-package
, so that
needs to be set up to install them if they aren't already.
(require 'use-package-ensure)
(setq use-package-always-ensure t)
UI
The start-up message gets pretty annoying, so disable that.
(setq inhibit-startup-screen t)
I like a little more line spacing than default.
(setq-default line-spacing 0.2)
Also, the menu-, tool- and scroll-bar are ugly, take up space and I don't use them.
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
It's nice to have an inverse of C-x o
for switching between
windows. other-window
, the function that C-x o
is bound to,
takes an argument COUNT
that determines how many windows it skips
forwards so we can simply pass -1 to other-window
in a lambda and
bind to that:
(global-set-key (kbd "C-x O")
(lambda ()
(interactive)
(other-window -1)))
Colour Scheme
Currently using spacemacs-theme
's light variant, but I prefer a pure
white background to the off-white it has by default.
(use-package spacemacs-theme
:defer t)
(setq spacemacs-theme-custom-colors
'((bg1 . "#ffffff")
(comment-bg . "#ffffff")))
(load-theme 'spacemacs-light t)
Autocompletion
Enable company-mode
globally, and hook it into completion-at-point-functions
.
(use-package company
:config
(add-hook 'after-init-hook 'global-company-mode)
(add-to-list 'company-backends 'company-capf))
And enable ido-mode
everywhere, with flexible matching.
(use-package ido
:config
(setq ido-enable-flex-matching t)
(add-hook 'after-init-hook
(lambda ()
(ido-everywhere)
(ido-mode t))))
Org
I use a couple non-standard bits and pieces, but not a whole
bunch. I really like the <s
to insert a source block thing (which
was deprecated); org-tempo
brings that back.
(use-package org
:ensure org-plus-contrib
:config
(require 'org-tempo))
A keybinding to add a new heading is super useful
(add-hook 'org-mode-hook
(lambda ()
(define-key org-mode-map
(kbd "<C-M-return>")
'org-insert-heading-after-current)))
Org is nice for scratch space
(setq initial-major-mode 'org-mode)
(setq initial-scratch-message "")
Source Blocks
Pressing tab inside a source block should indent appropriately for its language.
(setq org-src-tab-acts-natively t)
babel
lets us evaluate Org documents containing source blocks!
I've left the enabling of this for most languages to the section
for that language, but I'll add Emacs Lisp and shell here.
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(shell . t)))
By default trying to execute a source block prompts you, which is
super annoying since I'm realistically not going to try to run any
code from Org documents I haven't written, so that needs
disabling. You can do that by setting org-confirm-babel-evaluate to
nil
.
(setq org-confirm-babel-evaluate nil)
Another annoying thing that happens by default is the clobbering of the window layout when you open a source block. You can change that by setting org-src-window-setup.
(setq org-src-window-setup 'split-window-below)
Asyncronous Execution
ob-async
makes source blocks with the :async
keyword execute
asyncronously, super handy for long-running snippets etc.
(use-package ob-async)
Exporting
I very rarely want a table of contents, as most of my org documents are pretty short.
(setq org-export-with-toc nil)
HTML
htmlize
is needed for decent HTML exporting, but there is no need
for all that stuff at the bottom.
(use-package htmlize)
(setq org-html-postamble nil)
LaTeX
Use minted
(LaTeX package) to do syntax highlighting in code blocks:
(add-to-list 'org-latex-packages-alist '("" "minted"))
(setq org-latex-listings 'minted)
minted
actually calls pygments
through the shell, which pdflatex
doesn't like; you have to tell it not to worry, and that everything is
going to be OK.
(setq org-latex-pdf-process
'("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
Roam
A Zettelkasten in org mode? Yes please. It does need sqlite3
installed outside of Emacs land.
(use-package org-roam)
As stated in the manual, org-roam needs to know where notes are
stored. I'm going to go with ~/org/zet
, as its nice having all
org documents under ~/org
, but also we need to distinguish
zettels from other org stuff.
(make-directory "~/org/zet" t)
(setq org-roam-directory "~/org/zet")
And, also as recommended, we'll start org-roam-mode after init:
(add-hook 'after-init-hook 'org-roam-mode)
Hook it into Ido.
(setq org-roam-completion-system 'ido)
Default Applications
It's all fun and games until C-c C-e h o
opens the source code.
(setq org-file-apps
'(("html" . "firefox %s")
(auto-mode . emacs)))
Version Control
Git
magit
is truly a wonderful creation! Only deviations from defaults
here are a keybinding for magit-status
and a maximum length for the
summary line of commit messages (after which the excess is
highlighted).
(use-package magit
:bind
("C-x g" . magit-status)
:config
(setq git-commit-summary-max-length 72))
Language Integrations
Generic
Generally, 8-character-wide tabs are not my thing.
(setq-default tab-width 4)
(setq-default basic-offset 4)
And generally indenting with spaces is more common, so make that the default:
(setq-default indent-tabs-mode nil)
C
For C, I like to indent with tabs and align with spaces: this
behaviour is provided by smart-tabs-mode
.
(use-package smart-tabs-mode)
(smart-tabs-insinuate 'c)
I'll generally format my code in BSD style but I also use
clang-format
a lot, so I have a keybinding to run that.
(setq c-default-style "bsd")
(setq c-basic-offset 4)
(use-package clang-format)
(add-hook 'c-mode-hook
(lambda ()
(define-key c-mode-map (kbd "C-M-f")
'clang-format-buffer)))
Meson is my build system of choice for C, but I also use CMake a lot.
(use-package meson-mode)
(use-package cmake-mode)
Code Navigation
Using GNU Global for now, so hook ggtags-mode
into c-mode
:
(use-package ggtags
:config
(add-hook 'c-mode-common-hook
(lambda () (ggtags-mode 1))))
And, of course, add some keybindings
(define-key ggtags-mode-map (kbd "C-c g r") 'ggtags-find-reference)
(define-key ggtags-mode-map (kbd "C-c g d") 'ggtags-find-definition)
(define-key ggtags-mode-map (kbd "C-c g u") 'ggtags-update-tags)
Haskell
My workflow with Haskell is very REPL-based, so I always want
interactive-haskell-mode
on.
(use-package haskell-mode)
(require 'haskell-interactive-mode)
(add-hook 'haskell-mode-hook 'interactive-haskell-mode)
And, of course, that REPL needs to be taking advantage of parallelism!
(require 'haskell-process)
(set-variable 'haskell-process-args-ghci
'("-threaded" "+RTS" "-N8" "-RTS"))
Idris
The only thing to change from the defaults here is to add a more convenient way to case-split.
(use-package idris-mode)
(add-hook 'idris-mode-hook
(lambda ()
(define-key idris-mode-map (kbd "C-c SPC")
'idris-case-split)))
Rust
I never really use Rust without Cargo, so always turn on the minor mode for Cargo in Rust buffers.
(use-package rust-mode)
(use-package cargo)
(add-hook 'rust-mode-hook 'cargo-minor-mode)
Lisps
Common Lisp
Use SLIME and Quicklisp for Common Lisp (SBCL).
(use-package slime)
(setq inferior-lisp-program "sbcl")
(global-set-key (kbd "C-c s") 'slime-selector)
(load (expand-file-name "~/quicklisp/slime-helper.el"))
This assumes that SBCL and Quicklisp, along with Quicklisp's SLIME helper, are installed. Once SBCL is installed, Quicklisp and its SLIME helper can be installed by grabbing the installer and loading it with SBCL.
curl -O https://beta.quicklisp.org/quicklisp.lisp
sbcl --load quicklisp.lisp
That will open a REPL with the quicklisp-quickstart
system
loaded. At that REPL, run:
(quicklisp-quickstart:install)
(ql:add-to-init-file)
(ql:quickload "quicklisp-slime-helper")
Source blocks
To enable execution of CL source blocks in Org mode, add an item to org-babel-load-languages.
(org-babel-do-load-languages
'org-babel-load-languages
'((lisp . t)))
Paredit
paredit
is generally very useful for balancing parenthesis so we
want that turned on for all the lisps. Additionally, it's nice to have
an entire expression highlighted when the cursor is on one of its
enclosing parens.
(use-package paredit)
(setq lispy-mode-hooks
'(emacs-lisp-mode-hook
lisp-mode-hook
racket-mode-hook
scheme-mode-hook
slime-repl-mode-hook))
(dolist (hook lispy-mode-hooks)
(add-hook hook (lambda ()
(setq show-paren-style 'expression)
(paredit-mode))))
Scheme and Racket
Geiser is a pretty complete collection of Scheme things for Emacs. Only change from the defaults is to open the REPL in the current window instead of creating a new one.
(use-package geiser
:config
(setq geiser-repl-use-other-window nil))
YAML
I don't really like YAML if I'm honest, but it's used a lot so…
(use-package yaml-mode)
Javascript
The first bit of this setup (js2-mode
, js2-refactor
and
xref-js2
) is essentially copied from this Emacs cafe post.
First of all we want to grab js2-mode
and enable it for
javascript buffers. It extends the default js-mode
and builds an
AST which can be used by other packages.
(use-package js2-mode)
(add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
Refactoring
js2-refactor
provides refactoring tools based of said AST, so
enable that and its keybindings:
(use-package js2-refactor)
(add-hook 'js2-mode-hook #'js2-refactor-mode)
(js2r-add-keybindings-with-prefix "C-c C-r")
It provides a kill function with nice semantics for Javascript – we definitely want that instead of the generic kill.
(define-key js2-mode-map (kbd "C-k") #'js2r-kill)
Find references / jump to definition
Then we get to xref-js2
, which adds stuff for jumping to
references and definitions (uses the ag
tool, so that must be
installed in the environment):
(use-package xref-js2)
js-mode
binds M-.
, which conflicts with xref-js2
so we need to unbind that:
(define-key js-mode-map (kbd "M-.") nil)
And hook it up to js2-mode
:
(add-hook 'js2-mode-hook (lambda ()
(add-hook 'xref-backend-functions #'xref-js2-xref-backend nil t)))
Using local tools from NPM
To use tools locally by NPM, there is add-node-modules-path
:
(use-package add-node-modules-path)
(eval-after-load 'js2-mode
'(add-hook 'js2-mode-hook #'add-node-modules-path))
Autoformatting
Prettier seems low-effort to set up :D
(use-package prettier-js)
(add-hook 'js2-mode-hook 'prettier-js-mode)
Prolog
prolog-mode
comes with Emacs, but .pl files are assumed to be
Perl (which I never use, it scares me), so we need to change that.
(add-to-list 'auto-mode-alist '("\\.pl\\'" . prolog-mode))