Emacs
About
Emacs is an interactive graphical emacs lisp interpreter that comes with many applications, but is primarily used as a text and code editor. It has one of the largest repositories of packages of any similar code editor such as vim or its fork neovim.
Features
Emacs is often valued as a general purpose programming environment. Its power comes from its
- Extensibility
- Automatic self-documenting behaviour
- Flexibility
- Syntax awareness
- language server protocol support
- potential for reproducible portable literate configurations
Doom Emacs
The Doom Emacs project provides a framework with a more beginner-friendly default configuration, and pre-configured modules for popular features (e.g. IDE-like features, note-taking, and time management). Since it uses pinning in its configuration for dependencies, it is possible to package Doom Emacs with nix (see nix-doom-emacs).
Installation
Several stable versions of emacs are available by default in nixpkgs, and unstable branches are also available via the community overlay. Also, see the NixOS manual entry for Emacs for additional details about installation and configuration.
With Home-Manager
home.nix
{
programs.emacs = {
enable = true;
package = pkgs.emacs; # replace with pkgs.emacs-gtk, or a version provided by the community overlay if desired.
extraConfig = ''
(setq standard-indent 2)
'';
};
}
See the Home-Manager manual for a full list of options.
Without Home-Manager
Emacs can be installed in the same way as other packages:
configuration.nix
{
environment.systemPackages = with pkgs; [
emacs # replace with emacs-gtk, or a version provided by the community overlay if desired.
];
}
Enabling the Emacs daemon
Many Emacs users run Emacs as a daemon and access Emacs via the emacsclient
command. NixOS provides a systemd
service to facilitate this, which you can enable by adding the following to your configuration.nix
:
configuration.nix
{
services.emacs = {
enable = true;
package = pkgs.emacs; # replace with emacs-gtk, or a version provided by the community overlay if desired.
};
}
Available Emacs variants
Stable (nixpkgs)
Emacs is available in nixpkgs under the names emacs
and emacs-gtk
.
Since 2022-09, the package called emacs
now installs the lucid toolkit instead of gtk. The reason is that emacs is less stable with gtk especially in daemon mode. However, the lucid flavor of emacs will not take into account the gtk theme (since it is not even gtk) and looks quite… ugly (see comparisons here). If you still prefer the gtk version of emacs, you can instead install emacs-gtk
(before 2022-09 this package does not exist and emacs defaults to the gtk version).
Unstable (community overlay)
The community overlay provides nightly versions of the Emacs unstable branches, ELPA / MELPA packages, and EXWM + its dependencies. To use these, first apply the overlay (instructions below), which will make the packages available in nixpkgs. Then you can follow the normal nixpkgs installation instructions (above), but use your package of choice from the overlay (e.g. pkgs.emacsGit
) in place of pkgs.emacs
. See the README for a complete list of packages provided, and their differences.
With flakes
Using a system flake, one can specify the specific revision of the overlay as a flake input, for example:
inputs.emacs-overlay.url = "github:nix-community/emacs-overlay/da2f552d133497abd434006e0cae996c0a282394";
This can then be used in the system configuration by using the self
argument:
nixpkgs.overlays = [ (import self.inputs.emacs-overlay) ];
Without flakes
For installing one of the unstable branches of emacs, add the following lines to /etc/nixos/configuration.nix
:
configuration.nix
{
nixpkgs.overlays = [
(import (builtins.fetchGit {
url = "https://github.com/nix-community/emacs-overlay.git";
ref = "master";
rev = "bfc8f6edcb7bcf3cf24e4a7199b3f6fed96aaecf"; # change the revision
}))
];
}
Darwin (macOS)
Nixpkgs provides several of the "Mac Port" versions of Emacs, which have been patched to provide better integration with macOS (see the NixOS manual entry for a full list of packages). However, those packages typically track the stable releases of Emacs.
If you would like to use the latest version of Emacs on Darwin, one option is to use a package like emacsPgkt
from the community overlay (see above), and apply patches yourself via an override. For example, here is a derivation that applies the patches from the emacs-plus
homebrew formula:
pkgs.emacsPgtk.overrideAttrs (old: {
patches =
(old.patches or [])
++ [
# Fix OS window role (needed for window managers like yabai)
(fetchpatch {
url = "https://raw.githubusercontent.com/d12frosted/homebrew-emacs-plus/master/patches/emacs-28/fix-window-role.patch";
sha256 = "0c41rgpi19vr9ai740g09lka3nkjk48ppqyqdnncjrkfgvm2710z";
})
# Use poll instead of select to get file descriptors
(fetchpatch {
url = "https://raw.githubusercontent.com/d12frosted/homebrew-emacs-plus/master/patches/emacs-29/poll.patch";
sha256 = "0j26n6yma4n5wh4klikza6bjnzrmz6zihgcsdx36pn3vbfnaqbh5";
})
# Enable rounded window with no decoration
(fetchpatch {
url = "https://raw.githubusercontent.com/d12frosted/homebrew-emacs-plus/master/patches/emacs-29/round-undecorated-frame.patch";
sha256 = "111i0r3ahs0f52z15aaa3chlq7ardqnzpwp8r57kfsmnmg6c2nhf";
})
# Make Emacs aware of OS-level light/dark mode
(fetchpatch {
url = "https://raw.githubusercontent.com/d12frosted/homebrew-emacs-plus/master/patches/emacs-28/system-appearance.patch";
sha256 = "14ndp2fqqc95s70fwhpxq58y8qqj4gzvvffp77snm2xk76c1bvnn";
})
];
});
};
}
Window manager integration
Out of the box, non-"Mac Port" versions of Emacs will not be picked up properly by window managers like Yabai because Emacs does not set the correct macOS window role. This can be fixed with a patch (e.g. the first patch in the example above). However, even with the patch, Yabai may not correctly pick up Emacs if you invoke the emacs
binary directly from a shell. For Emacs to work properly with window managers you must invoke it by running the macOS app that is generated when you install Emacs with nix. You can setup an alias to do this like so (replace pkgs.emacs
with the package you are using):
home.nix
programs.zsh = {
enable = true;
shellAliases = {
emacs = "${pkgs.emacs}/Applications/Emacs.app/Contents/MacOS/Emacs";
};
};
Installing packages
One can mix and match whether Emacs packages are installed by Nix or Emacs. This can be particularly useful for Emacs packages that need to be built, such as vterm. One way to install Emacs packages through Nix is by the following, replacing emacsPgtkNativeComp
with the variant in use:
environment.systemPackages = with pkgs; [ ... ((emacsPackagesFor emacsPgtkNativeComp).emacsWithPackages ( epkgs: [ epkgs.vterm ] )) ... ];
To make the packages available to emacsclient
, one can do the following:
services.emacs.package = with pkgs; ( (emacsPackagesFor emacsPgtkNativeComp).emacsWithPackages ( epkgs: [ epkgs.vterm ] ) );
Note that some packages may have strange characters like +
that would be considered as a syntax error by nix. To avoid that, make sure to write it in quotes and to prepend it with the package set that you want l (even if you used with epkgs; …
) like epkgs."ido-completing-read+"
.
Automatic package management
If you use use-package
or leaf
in your configuration, the community overlay can manage your Emacs packages automatically by using emacsWithPackagesFromUsePackage
. First, install the overlay (instructions above), then add the following to your configuration.nix
:
{
environment.systemPackages = [
(pkgs.emacsWithPackagesFromUsePackage {
package = pkgs.emacsGit; # replace with pkgs.emacsPgtk, or another version if desired.
config = path/to/your/config.el;
# config = path/to/your/config.org; # Org-Babel configs also supported
# Optionally provide extra packages not in the configuration file.
extraEmacsPackages = epkgs: [
epkgs.use-package;
];
# Optionally override derivations.
override = epkgs: epkgs // {
somePackage = epkgs.melpaPackages.somePackage.overrideAttrs(old: {
# Apply fixes here
});
};
})
];
}
See the overlay README for a full list of options.
Adding packages from outside ELPA / MELPA
Some packages may require more sophisticated derivation, but the following is a good starting point for adding external packages:
lambda-line.nix
{
trivialBuild,
fetchFromGitHub,
all-the-icons,
}:
trivialBuild rec {
pname = "lambda-line";
version = "main-23-11-2022";
src = fetchFromGitHub {
owner = "Lambda-Emacs";
repo = "lambda-line";
rev = "22186321a7442f1bd3b121f739007bd809cb38f8";
hash = "sha256-2tOXMqpmd14ohzmrRoV5Urf0HlnRPV1EVHm/d8OBSGE=";
};
# elisp dependencies
propagatedUserEnvPkgs = [
all-the-icons
];
buildInputs = propagatedUserEnvPkgs;
}
You can then use the new package with automatic package management like so:
configuration.nix
{
environment.systemPackages = [
(pkgs.emacsWithPackagesFromUsePackage {
...
override = epkgs: epkgs // {
lambda-line = callPackage ./lambda-line.nix {
inherit (pkgs) fetchFromGitHub;
inherit (epkgs) trivialBuild all-the-icons;
};
};
})
];
}
or manual package management like so:
configuration.nix
{
environment.systemPackages = with pkgs;
[ ...
((emacsPackagesFor emacsPgtkNativeComp).emacsWithPackages (epkgs: [
epkgs.vterm
(callPackage ./lambda-line.nix {
inherit (pkgs) fetchFromGitHub;
inherit (epkgs) trivialBuild all-the-icons;
};)
]))
...
];
}
Packaging and testing emacs nixpkgs
Emacs packages can be defined and tested like other nixpkgs. They can be obtained from melpa, elpa or other sources such as github.
default.nix
{ trivialBuild
, lib
, fetchFromGitHub
...
}:
trivialBuild {
pname = "...";
version = "...";
src = fetchFromGitHub {
owner = "...";
repo = "...";
rev = "...";
sha256 = "...";
};
packageRequires = [ ... ];
patches = [ ... ];
meta = {
description = "...";
license = lib.licenses.gpl3;
platforms = lib.platforms.all;
};
}
They are located at pkgs/applications/editors/emacs/elisp-packages/manual-packages/ and a new pkg must be added under pkgs/applications/editors/elisp-packages/manual-packages.nix.
Once the nixpkg is ready, it can be tested using the following command. This inserts the nixpkg into the load-path of emacs.
nix-shell -I nixpkgs=<path_to_nixpkgs_copy> -p "(emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [ epkgs.<package> ])"
Bugs
Plasma taskbar grouping
To fix/workaround Plasma grouping emacs incorrectly (confusing emacs.desktop with emacsclient.desktop), perform the following: 1. open emacs 2. right click title bar 3. More Actions > Configure Special Window Settings 4. click Add Property > Desktop File Name 5. set desktop file name to "/home/<USERNAME>/.nix-profile/share/applications/emacs.desktop" 6. hit OK 7. restart emacs if need
all emacs instances should now be grouped together, allowing you to pin it and reliably switch to it with Super+<number>
Spell checking
Because emacs expects the dictionaries to be on the same directory as aspell, they won't be picked up. To fix it install the aspellWithDicts
package, specifying the dictionaries you want to use:
configuration.nix
{
environment.systemPackages = with pkgs; [
(aspellWithDicts (dicts: with dicts; [ en en-computers en-science es]))
];
}
A list of official dictionaries for aspell can be found on Aspell Website
Cannot find all binaries on a remote system with TRAMP
Emacs uses tramp-remote-path
to know what paths to search binaries under in a remote system. This in turn relies on the output of getconf PATH
which does not return all the paths available under $PATH
, especially if one uses home-manager.
To fix this, add the following snippet in your emacs config:
(require 'tramp-sh) (setq tramp-remote-path (append tramp-remote-path '(tramp-own-remote-path)))
- Previous reference: https://releases.nixos.org/nix-dev/2013-June/011250.html
- This bug was raised in home manager: https://github.com/nix-community/home-manager/issues/5275