quantity <[across names]>quantity <[across names]># Tycho Emacs Config Kit
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.
[starter kits]:https://github.com/emacs-tw/awesome-emacs#starter-kit)
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, andrecentfconfig, but also omits these features for ephemeral sessions. Also daemon-related state files are saved in~/.emacs.dfolders 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,rst-modeandmarkdown-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. Current timing (on my systems) is around 750ms (and often less,) for GUI sessions and less than 400ms for terminal sessions.
-
a compelling (and roughly equivalent) terminal experience and GUI experience.
-
superior completion experience. While I've historically been a helm user (and that configuration is still in the tree in the
legacy/directory), in mid 2025, I switched to using the vertico + consult + corfu + cape + embark + marginalia. Though there are more pieces, but having a less monolithic approach makes the whole system a bit simpler, and also easy to customize more directly. -
use the red/green friendly colorblind friendly modus themes--now included with emacs--by the default.
-
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 of sessions. Add support for an
--idCLI flag to be able to maintain multiple distinct emacs sessions on a single system, either as named daemons or just default from the command line. -
solid configurations for other configurations: yasnippet for text expansion, and eglot for getting rich IDE-like support language servers,
tree-sitterfor syntax handling, and more recently I've been exploring various ways to integrate LLM-derived coding tools. Historically I've used more third-party packages for this kind of functionality (like lsp-mode,company-mode, and others), but I've tended toward using increasingly minimal configurations and very thin components.
Install Emacs in whatever way makes sense for you and your system. My preferences are:
-
Emacs 30 or greater. There's not a lot of reason to hang out with old versions.
-
Lucid. This is a bit odd, but there's a long standing GTK/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 and getting a build with a native (coca? do we still call it that?) toolkit. The last time I tried this (Aug 2025), it worked well.
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 installgoplsusinggo get. -
To read email, I install
mu(which includesmu4e) from the 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.archiveThen 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 --initIn 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 30 (or whatever version I'm running these days)
the elpa checkout might not work. In the past I've committed major-version
branches for different versions of emacs. 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 and builds 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-reexecNow you can use the following commands, to start two emacs daemons:
systemctl --user start emacs@personal
systemctl --user start emacs@workThe following command will ensure that the daemons start when your system reboots:
systemctl --user enable emacs@personal
systemctl --user enable emacs@workNow 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-x g sopens themagit-statusbuffer for the current repository. -
C-c t <...>(tfor 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 <...>for functions related to blog posting.
-
C-c g <...>for grep/git-grep/ag/rg helpers for searching for strings in directories projects. I tend to preferrgthese days and have commands for doing both incremental search (with consult) and in compile buffers (as withfind-grep. -
C-c w <>forbrowse-urlfunctionality opening links in various browserseis forewwandcis for chromium. -
C-c lis the prefix for alleglotkeybindings, I'm particularly fond ofC-c l s sto 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 fenables flycheck, and all flycheck keybindings are underC-c f, so "open list of flycheck buffers"C-c f l. -
C-c t cruns build at the top-level of the current project, providing the ability to manage a few different compile buffers, to be able to run test, lint, build (and other) in parallel. For many languages it will also suggest commands to run. Inside of a compile buffer theCkey will change the compile command. I use this for managing most background processes. -
C-c .provides access to specific completion suggestions. This should happen automatically as you type, but you can look for a specific kind of completion or to 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 specifically.
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-project-file-capture-templates "file-name" :prefix <char>)
;; using the empty key creates a shortcuts in a file at the top level
;; (templates starting with n, t, r, j )
(tychoish-org-add-project-file-capture-templates "meta" "m")
(tychoish-org-add-project-file-capture-templates "writing" "w")
(tychoish-org-add-project-file-capture-templates "blog" "b")
(tychoish-org-add-project-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
mu4eto manage email, in a multi-account setup. I wrote a macro,tychoish-define-mail-accountto add a new account. I have a few functions that I define here for changing my originating email address/mu database, which are bound to keys, but the basics are:
(tychoish-define-mail-account
:name "tycho"
:address "[email protected]"
:key "g"
:id "tychoish"
:instances '("hud")
:command "fetchmail")This binds C-m g to a function that changes the mu4e configuration to use this
account. The C-m a opens a menu where you can select an account.
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 development experience for specific languages (including English!) and other workflows, integration with external tools (e.g. like coding agents).