You can use Nix as a package manager for Emacs, like so:
{
home-manager.users.eudoxia = {
programs.emacs = {
enable = true;
extraPackages =
epkgs: with epkgs; [
magit
rust-mode
treemacs
# and so on
];
};
};
}
Today I learned you can also use it to create ad-hoc packages for things not in MELPA or nixpkgs.
The other day I wanted to get back into Inform 7, naturally the first
stack frame of the yak shave was to look for an Emacs
mode. inform7-mode exists, but isn’t packaged anywhere. So I had to
vendor it in.
You can use git submodules for this, but I have an irrational aversion to
submodules. Instead I did something far worse: I wrote a Makefile to
download the .el from GitHub, and used home-manager to copy it into my
.emacs.d. Which is nasty. And of course this only works for small, single-file
packages. And, on top of that: whatever dependencies your vendored packages need
have to be listed in extraPackages, which confuses the packages you want,
with the transitive dependencies of your vendored packages.
I felt like the orange juice bit from The Simpsons. There must be a better way!
And there is. With some help from Claude, I wrote this:
let
customPackages = {
inform7-mode = pkgs.emacsPackages.trivialBuild {
pname = "inform7-mode";
version = "unstable";
src = pkgs.fetchFromGitHub {
owner = "alexispurslane";
repo = "inform7-mode";
rev = "f99e534768c816ec038f34126f88d816c2f7d9ff";
sha256 = "sha256-r9Zzd8Ro3p+Bae11bf1WIeVWkbmg17RKLDqG4UcFT1o=";
};
packageRequires = with pkgs.emacsPackages; [
s
];
};
};
in
{
home-manager.users.eudoxia = {
programs.emacs = {
enable = true;
extraPackages =
epkgs: with epkgs; [
customPackages.inform7-mode
# ...
];
};
};
}
Nix takes care of everything: commit pinning, security (with the SHA-256 hash), dependencies for custom packages. And it works wonderfully.
Armed with a new hammer, I set out to drive some nails.
cabal-mode
Today I created a tiny Haskell project, and when I opened the .cabal file,
noticed it had no syntax highlighting. I was surprised to find there’s no
cabal-mode in MELPA. But coincidentally, someone started working on this
literally three weeks ago! So I wrote a small expression to package this
new cabal-mode:
cabal-mode = pkgs.emacsPackages.trivialBuild {
pname = "cabal-mode";
version = "unstable";
src = pkgs.fetchFromGitHub {
owner = "webdevred";
repo = "cabal-mode";
rev = "083a777e09bdb5a8d8d69862d44f13078664091f";
sha256 = "sha256-c5dUsnEx+0uXFzxQLMnhiP8Gvwedzvq0F0BA+beBkmI=";
};
packageRequires = [ ];
};
xcompose-mode
A few weeks back I switched from macOS to Linux, and since I’m stuck on X11
because of stumpwm, I’m using XCompose to define keybindings for
entering dashes, smart quotes etc. It bothered me slightly that my
.XCompose file didn’t have syntax highlighting. I found
xcompose-mode.el in kragen’s xcompose repo, but it’s
slightly broken (it’s missing a provide call at the end). I started thinking
how hard it would be to write a Nix expression to modify the source after
fetching, when I found that Thomas Voss hosts a patched version
here. Which made this very simple:
xcompose-mode = pkgs.emacsPackages.trivialBuild {
pname = "xcompose-mode";
version = "unstable";
src = pkgs.fetchgit {
url = "git://git.thomasvoss.com/xcompose-mode.git";
rev = "aeb03f9144e39c882ca6c5c61b9ed1300a2a12ee";
sha256 = "sha256-lPapwSJKG+noINmT1G5jNyUZs5VykMOSKJIbQxBWLEA=";
};
packageRequires = [ ];
};
eat
Somehow the version of eat in nixpkgs unstable was missing the
configuration option to use a custom shell. Since I want to use nu instead of
bash, I had to package this myself from the latest commit:
eat = pkgs.emacsPackages.trivialBuild {
pname = "eat";
version = "unstable";
src = pkgs.fetchgit {
url = "https://codeberg.org/akib/emacs-eat.git";
rev = "c8d54d649872bfe7b2b9f49ae5c2addbf12d3b99";
sha256 = "sha256-9xG2rMlaMFY77JzUQ3JFrc7XKILZSL8TbP/BkzvBvMk=";
};
packageRequires = with pkgs.emacsPackages; [
compat
];
};
lean4-mode
I started reading Functional Programming in Lean recently, and while
there is a lean4-mode, it’s not packaged anywhere. This only required a
slight deviation from the pattern: when I opened a .lean file I got an error
about a missing JSON file, consulting the README for lean4-mode, it says:
If you use a source-based package-manager (e.g.
package-vc.el, Straight or Elpaca), then make sure to list the"data"directory in your Lean4-Mode package recipe.
To do this I had to use melpaBuild rather than trivialBuild:
lean4-mode = pkgs.emacsPackages.melpaBuild {
pname = "lean4-mode";
version = "1.1.2";
src = pkgs.fetchFromGitHub {
owner = "leanprover-community";
repo = "lean4-mode";
rev = "1388f9d1429e38a39ab913c6daae55f6ce799479";
sha256 = "sha256-6XFcyqSTx1CwNWqQvIc25cuQMwh3YXnbgr5cDiOCxBk=";
};
packageRequires = with pkgs.emacsPackages; [
dash
lsp-mode
magit-section
];
files = ''("*.el" "data")'';
};