I've been using Emacs for a long time, and I suspect I will be for a while yet. This repository contains all of that configuration, packaged up in a way that anyone can use, learn from, and get started using very quickly. There are a lot of starter kits for emacs, and this isn't quite that; although if you're newer to Emacs and want to bootstrap the configuration process, or if you've been using Emacs a bit for a while but aren't particularly fond of your configuration, this might be a good place to start.
Fair warning, where starter kits are flashy and opinionated, this configuration strives for a exceptionally minimal and lightweight experience that's also fully usable and feature complete as your primary text-editing and software development toolkit.
From a high level, the goals are:
- exceptionally fast start times: on my (not very new) computers, Emacs sessions readily start in about a second, and while I've included systemd service files for managing daemons, being able to start ephemeral sessions and have Emacs associated with opening specific file types is useful.
- full support for console-mode Emacs: while I mostly use Emacs in GUI mode, I've always been envious of vim user's ability to operate contently in the console. The configuration omits some features which are annoying on the console, makes it possible to use themes in Emacs daemons without impacting the experience on the console.
- daemon-first and multi-daemon support without compromises: the Emacs
daemon, allows you to have a single Emacs session and open different frames
that have access to the same state, and persist even if all frames
close. This configuration provides support for saving this state--even
across restarts of the daemon using a reasonable
desktop
,session
, andrecentf
config, but also omits these features for ephemeral sessions. Also daemon-related state files are saved in~/.emacs.d
folders with names specific to the daemon instance so you can have some degree of isolation between environments. - visually minimal: I turned everything off, there are no menus, and Emacs doesn't talk to you about anything.
- great initial experience: I've provided mechanisms for automatically loading your own arbitrary configuration, and attempted to document my own configuration as much as possible; however, I've tried to put good defaults in place and made it super easy to get started without any required customization.
- support all of my common workflows: By day I write a lot of Go, with
some other programming in Common Lisp, TypeScript/JavaScript, Python, Shell,
and whatever else comes up. I also write a bunch of English in
org-mode
andrst-mode
. I also read email in emacs. All of these use cases should be well supported.
This is not an exhaustive list:
- compellingly fast load times. My goal is to keep 3-10 year old computers starting emacs in well under 2 seconds.
- helm for everything, and lots of convent bindings to put helm menus at your finger tips.
- red/green friendly colorblind friendly themes (accessible via
tychoish-load-dark-theme
andtychoish-load-light-theme
,) but not enabled by default. They are the modus themes . - good support for long-lived emacs sessions, both with running multiple daemons on a single system, as well as configuration to support simple session saving and automatic restoration.
- rich editing support: yasnippets for text snippets, company-mode for automatic completion as you type, and lsp-mode for all of the rich IDE-like features.
Install Emacs in whatever way makes sense for you and your system. My preferences are:
- Emacs 27.1 or greater. The latest version of Emacs has very fast JSON parsing, which makes using lsp-mode compelling.
- Lucid. This is a bit odd, but there's a long standing GK/Emacs issue where GTK Emacs daemons crash if the X11 server restarts. It's not a big deal, and totally irrelevant if you're not planning to use the daemon mode, but it's there.
When I've used macOS in the past I've always installed emacs using homebrew, though I'm not sure what the state of the art is there: ideally building something with the cocoa bits is nice.
Other dependencies, all optional, depending on your goals:
- If you want to use LSP mode (you probably do) you will probably want to
select and install language servers with packages from your operating
system. On Arch I have
clang
,typescript-language-server-bin
,bash-language-server
,dockerfile-language-server-bin
,python-langauge-server
. I also installgopls
usinggo get
. - To read email, I install
mu
(which includesmu4e
) from arch packages.
Basically you want this repository to be your ~/.emacs.d
directory. I'd go
about it by, first moving your existing ~/.emacs.d/
out of the way:
mv ~/.emacs.d/ ~/emacs.d.archive
Then clone the repository:
git clone --recurse-submodules [email protected]:tychoish/.emacs.d.git ~/.emacs.d/
And that's it! the --recursive-submodules
option clones an elpa
submodule, which it totally optional, but means that you won't have to
download all of the packages on the first time.
In more advanced setups, you can choose to break this apart:
git clone [email protected]:tychoish/.emacs.d.git ~/.emacs.d/
I've created a repository that just has a clone of my elpa
(package)
directory as a submodule. This is optional, but it will be quicker and
any bugs you run into I'll probably be hitting as well:
cd ~/.emacs.d/ git submodule update --init
In the future you can just pull/merge from the upstream to get updates, and
run git submodule update
to update elpa
.
Read the customization section for more information. It's possible to put your
own config in the users
subdirectory, but you might also want to fork this
repository and make your own modifications, or have more control over how you
track the mainline.
If you're not running emacs 27.1 the elpa
checkout might not
work. There's a branch in the repository named emacs28
that should
support the master branch of emacs, but you can omit the
--recursive-submodules
option or remove the elpa
directory entirely:
Emacs will take much longer to start the first time as it downloads all of
the third-party packages it depends on.
Once you're installed, emacs
should just read the tychoish
configuration. I would start by using emacs
(GUI) and emacs -nw
(console) to start emacs
, or just by selecting it from the menus in your
desktop environment. Once you've gotten used to things, you can configure the
daemon mode as follows:
If you're on Linux, and want to use the systemd --user
instance, which is
like your own user-specific systemd instance, begin by using the following
command to ensure that systemd --user
instance starts on boot and doesn't
wait for you to login:
sudo loginctl enable-linger $(whoami)
Then copy the relevant service file into the ~/.config/systemd/user/
directory:
cp ~/.emacs.d/[email protected] ~/.config/systemd/user/
Then run the following configuration to reload the systemd
instance:
systemctl --user daemon-reload systemctl --user daemon-reexec
Now you can use the following commands, to start two
emacs
daemons:
systemctl --user start emacs@personal systemctl --user start emacs@work
The following command will ensure that the daemons start when your system reboots:
systemctl --user enable emacs@personal systemctl --user enable emacs@work
Now you can start the emacsclient
which opens Emacs frames attached to the
specified daemon. I keep the following aliases in my shell, and bound to
keybindings in my window manager as well:
alias e='emacsclient --server-file=personal --no-wait' alias ew='emacsclient --server-file=personal --create-frame --no-wait' alias et='emacsclient --server-file=personal --tty' alias we='emacsclient --server-file=work --no-wait' alias wew='emacsclient --server-file=work --create-frame --no-wait' alias wet='emacsclient --server-file=work --tty'
Modify these commands to use whatever daemon names you selected above. The
e
option opens a specific file in the most recent frame you've used,
ew
creates a new frame optionally opening a file, and et
opens a
console window optionally opening a file.
Most of the keybindings are defined in package specific configuration in
lisp/tychoish-coreprogramming.el
. In daemon/GUI-mode, the which-key makes these discoverable. I
often use helm menus to find hints about keybindings. Some broad themes:
C-c h <mnemonic>
provide entry-points into helm-based functionality.a
for appropos is great for finding documentation of symbols for emacs lisp;s
gets atswoop
which is a buffer search tool (alsoC-c M-s
)``;m
opens system man pages. Other helm keybindings:C-x m
,C-x C-m
are alternates forM-x
and do similar things.M-<spc>
opens ahelm-mini
that searches across buffers, files, and commands.C-c a p
does a helm-ag search in the current project.C-c r p
does the same thing but with ripgrep.C-c a s
andC-c r s
do the same thing but starting at the directory of the current file.
C-x g s
opens themagit-status
buffer for the current repository.C-c t <mnemonic>
(t
for tycho) provide entry-points into functions that I've written or cases where I want quicker access to something that isn't bound by default:C-c t t <d|l|e>
for disable, load, enable theme.C-c t b <mnemonic>
for functions related to blog posting.
C-c g <mnemonic>
for grep/git-grep/ag/rg helpers for searching for strings in directories projects.C-c w <mnemonic>
forbrowse-url
functionality opening links in various browserse
is forWWW
andc
is for chromium.C-c l
is the prefix for alllsp-mode
keybindings, I'm particularly fond ofC-c l s s
to start an lsp session on a file.C-c f =
andC-c f -
to increase the text size in a specific buffer.C-c f f
enables flycheck, and all flycheck keybindings are underC-c f
, so "open list of flycheck buffers"C-c f l
.C-c t c
runsmake build
at the top-level of the current file in a unique compile buffer, switching to that buffer if its open, useC
in compile-mode to change the compile command. I use this for managing most background processes.C-c .
andC-c C .
open a company completion window at the current point on demand. These open automatically many times, but it's nice to be able to call them up.C-c s s
opens a company window for inserting asnippets explicitly, which are intentionally the lowest priority completions.
The process of "making my config public and reusable" mostly centered on pulling all of the assumptions about the way I organize my files and paths, and avoid hardcoding things in most places. The result is that there isn't much customization that you should need to do; however, the following variable should probably be set differently:
(setq local-notes-directory (expand-file-name "~/notes")))
The local-notes-directory
is the top level directory underwhich org-mode
,
deft
and roam
directories are stored in this configuration.
For any further customization, create or link files in the
~/.emacs.d/user/
directory and they'll be loaded after my config finishes
loading. These files should have matching file names and feature declarations,
as in a (provide 'file)
form at the bottom for file.el
. These files
can use elisp that's provided elsewhere in the config, but you should rely on
any specific initialization order for these files. The code that loads them
also prints the load times so you can see if you're loosing too much time
loading this code: having lots of files here can really impact your load time,
one or two files is fine, but more could be a problem.
I keep work-specific configuration here (just to keep it separate from the rest of the configuration,) as well as configuration that's super specific to my machines or personal use (e.g email.) Consider some of the following configuration:
Setup my org capture templates, using a function defined elsewhere:
;; (tychoish-org-add-file-capture-templates <prefix-key> "file-name") ;; using the empty key creates a shortcuts in a file at the top level ;; (templates starting with n, t, r, j ) (tychoish-org-add-file-capture-templates "m" "meta") (tychoish-org-add-file-capture-templates "w" "writing") (tychoish-org-add-file-capture-templates "b" "blog") (tychoish-org-add-file-capture-templates "" "organizer")
This modifies data in a list, so calling this function in the reverse priority is ideal.
Setup different fonts on different machines:
(when (gui-p) (let ((sys (system-name))) (cond ((equal "deleuze" sys) (tychoish-setup-font "Source Code Pro" 11)) ((equal "derrida" sys) (tychoish-setup-font "Source Code Pro" 10)) ((equal "bakhtin" sys) (tychoish-setup-font "Source Code Pro" 10)))))
I use different font sizes on my laptop
deleuze
but at the very least, if you do nothing else I'd use one of the following forms somewhere:(when (gui-p) (tychoish-setup-font "Source Code Pro" 10)) (when (gui-p) (tychoish-setup-font "Inconsolata-g" 11)) (when (gui-p) (tychoish-setup-font "Consolas" 11))
tychoish-setup-font
also works interactively.For using
mu4e
to manage email, in a multi-account setup, I have a few functions that I define here for changing my originating email address/mu database, which are bound to keys, but the fundamentals are:(defun tychoish-mail-setup-personal () (interactive) (setq mu4e-get-mail-command "fetchmail -a") (setq mu4e-user-mail-address-list '("[email protected]" "[email protected]")) (tychoish-set-up-email (expand-file-name "~/mail") "tycho garen" "[email protected]")) (global-set-key (kbd "C-c m a") ''tychoish-mail-setup-personal) (defun tychoish-mail-setup-work () (interactive) (setq mu4e-get-mail-command "fetchmail -a") (setq mu4e-user-mail-address-list '("[email protected]")) (tychoish-set-up-email (expand-file-name "~/mail") "sam tycho garen" "[email protected]")) (global-set-key (kbd "C-c m a") ''tychoish-mail-setup-work) (let ((daemon (daemonp))) (cond ((equal daemon "personal") (tychoish-mail-personal)) ((equal daemon "work") (tychoish-mail-work))))
Enjoy! Happy hacking!
If you like this, and find it useful, you don't need to do anything! Just enjoy! If you discover a bug, or have a feature request, please feel free to open an issue or submit a pull request!
I'm not particularly sure what direction development will take, but I'm interested in the following areas:
- Improving the way that console mode interacts with themes.
- Increasing or maintaining the current level with regards to startup time.
- Continue to improve the experience with lsp-mode, to make it feel more automatic.