How to put custom emacs setup in configuration.nix

Hello,

I would like to put some thing like this (nixpks config overide)

myEmacs = pkgs.emacs.pkgs.withPackages (epkgs: (with epkgs.melpaStablePackages; [
      company
      counsel
      flycheck
      ivy
      magit
      projectile
      use-package
    ]));

directly into my configuration.nix rather than in ~/.config/nixpkgs/config.nix .

And … umm … would appreciate some guidance.

Thanks :slight_smile:

See https://nixos.wiki/wiki/Emacs.

I’ve been using

      environment.systemPackages = with pkgs; [
        ((emacsPackagesNgGen emacs).emacsWithPackages (epkgs: [ epkgs.vterm ])) ]

in my configuration.nix.

Thanks for your reply :slight_smile:
I ended up adopting more or less the same the same approach as you:

...

  environment.systemPackages = with pkgs; [
    git
    htop
    (pkgs.emacs.pkgs.withPackages (epkgs: (with epkgs.melpaStablePackages; [
      company
      counsel
      flycheck
      julia-mode
      ivy
      magit
      projectile
      use-package
    ])))
    wget
  ];
...

And am currently wondering if I can assign the emacs stuff to a variable e.g.

myemacs = pkgs: ...

environment.systemPackages = with pkgs; [
    git
    htop
    myemacs
    wget
  ];

where myemacs perhaps uses super/self as in here.
I am also curious about putting my emacs stuff in a seaprate file and using import a la:


environment.systemPackages = with pkgs; [
    git
    htop
    (import ./my-emacs.nix)
    wget
  ];

:thinking:

1 Like

You’ll want to define a derivation for emacs. If you’re going down that road, I’d suggest creating a pkgs subdirectory (because you’ll be writing more packages of your own in time :slight_smile: ).

Then start off with a default.nix in your pkgs directory (default.nix is automatically imported if you import a directory, i.e. import ./pkgs in this case):

{ pkgs }:

let
  # callPackage automagically provides the arguments we ask for
  # from `pkgs`, see the nix pills for how it works. Also note
  # `callPackagesWith` exists, that allows us to extend this.
  inherit (pkgs) callPackage;

in {
  emacs = callPackage ./emacs.nix { };
}

emacs.nix:

{ emacs, emacsPort, hostPlatform }:

let
  # For bonus points so we support Mac OS as well
  emacsPlatform = if hostPlatform.isDarwin then emacsMacport else emacs;

in emacsPlatform.pkgs.withPackages (epkgs: with epkgs.melpaStablePackages; [
  company
  counsel
  flycheck
  julia-mode
  ivy
  magit
  projectile
  use-package
])

Then, at your top-level (assuming configuration.nix, but a flake might be cleaner), create an overlay for your packages and tell nixpkgs to use it:

{ pkgs, ... }:

let
  overlays = [
    # If you want to completely override the normal package
    # (prev: final: import ./pkgs { inherit pkgs; })
    # If you want to access your package as `local.emacs`
    (prev: final: {
      local = import ./pkgs { inherit pkgs; };
    })
    # `prev: final:` is my preference over `super: self:`; these are just
    # names, but I think mine are clearer about what they mean ;)

    # You can also use `my` instead of `local`, of course, but I dislike
    # that naming convention with a passion. At best, it should be
    # `our`.
  ];
in {
    # This will add our overlays to `pkgs`
    nixpkgs.overlays = overlays;

    environment.systemPackages = with pkgs; [
      git
      htop
      local.emacs # Or just `emacs` if you used the first overlay
      wget
    ];
}

If you insist, you can also just directly call import ./pkgs/emacs.nix { inherit (pkgs) emacs emacsPort hostPlatform; };, but having a proper place to put downstream packages is just much cleaner IMO.

I don’t think my config is clean enough for most people to learn from, but I do something like that: https://github.com/TLATER/dotfiles/blob/8cd281d6f062cf0ec07b4d47e87c2bba464a763d/nixpkgs/pkgs/emacs.nix

I also use an excellent little script provided by Bauer to parse which packages are use-package’d, so my config just automatically has the correct packages available :slight_smile: This takes some work on the emacs config side too, though. Also, as a bonus, a hint on how to override emacs packages, in case elpa decides to update tarballs without version changes again…

3 Likes

Hey thanks for this … it might take me a little time to digest.

Heh, very understandable, feel free to ask if you have questions :slight_smile:

Thanks this is great! Your inline comments are really useful… I also feel a little icky when writing my :-).

A simple question: in the above did you mean to write prev: final: rather than final: prev:?

A more open question: Having looked at your dotfiles and and nixos-hosts repos I have decided to use them as the basis for a revamp of my configuration. Is there anything I should watch out for?

Hah! No, of course not, I just can’t write correct code without testing to save my life. Sorry for the confusion - final: prev: should be the correct order.

I do some good things and some bad things. Some of the bad things are on this list: Anti-patterns — nix.dev documentation. Don’t repeat those mistakes (but I think very localized with pkgs for package lists is ok) :slight_smile:

At a high level, by now I believe the repositories could be much simpler and cleaner if I just had one instead of two, and structured all my flake outputs as subdirectories that I import instead (with a default.nix at the root of each directory). My original idea was that I’d still want to be able to use/present my dotfiles without nix (so I can point others at the more generic parts, and in case I decided to ditch nix at some point), which is how I ended up with the current structure. I was also too hesitant to create subdirectories for “small” bits of code (that have, as usual, grown).

I’m still partial to keeping my dotfiles very cleanly separate from my nixos config, but this can be done as described without splitting the repository, which would resolve some of the complexity in my update management. It would also reduce the complexity of the flake.nix boilerplate, which I’m finding more and more desirable. Having a complex flake.nix detracts from its usefulness as an overview of what the flake actually does.

If there’s something specific to be aware of, this is likely the biggest issue: dotfiles/flake.nix at f47850e93ef32a29401d4f1cec5ba1c9dec3fe2d · TLATER/dotfiles · GitHub - I believe it should be implemented as a nixosModule instead, but at the time I didn’t quite realize that was an option, so I wrote that, frankly, mess. Profiles are probably ok (though I find I’m not using them effectively), just don’t expose them on the flake level like that. If you want me to explain why I’m doing that, and how it should be done, feel free to PM me - I think despite my attempts at commenting about that I’ve not conveyed that very well in my repository.

On the positive side, I like my use of nvfetcher, and how I hook the fetched sources into my packages using callPackageWith. It keeps packages lean, and makes updating tidy and easy (it all runs in CI once a week).

I also like the checks I recently added, because they make setting up simple sanity checks super convenient (you’ve seen first hand that I need them) - very nice if you don’t like accidentally bricking your editor and having to roll back.

In general, if you’re planning on keeping this on github, I’d really recomend setting up actions like I do to give you regular update PRs. It really helps make sure you stay on top of updates. If you only do it locally, make sure you only ever have to run one command to get your system up to date. I wrote a script to help me with that, but that’s largely because of the double repo issue.

Specific to emacs, my config is super convenient: dotfiles/emacs.nix at f47850e93ef32a29401d4f1cec5ba1c9dec3fe2d · TLATER/dotfiles · GitHub. It uses a script from GitHub - matthewbauer/bauer: An Emacs+Nix IDE, which automatically determines packages I depend on with use-package, and makes sure they’re installed. This is awesome, but unfortunately it relies on IFD. I haven’t made my mind about whether this trade-off is worth it yet.

I will spend some time and let you know how I get on.
Many thanks for your help!

This is a tangential question, but I asked on Matrix without answer.

I would like to use nix-doom-emacs and bundle Emacs config and deployment with the system itself. But since native-comp, every NixOS deployment natively compiles all 200+ packages and 1500+ files in my doom config and waiting for 10+ min n every rebuild gets tiring fast.

Is there a way to use nix-doom-emacs without native-compiling ahead of time?

I assume you mean using https://www.gnu.org/software/emacs/manual/html_node/elisp/Native_002dCompilation-Functions.html?

You’d have to generate derivations for each lisp package, and then host them on a binary cache somewhere, probably as an overlay of some sort. Far from impossible, but it would take some development. I don’t know of existing solutions for this, but there’s a fairly big emacs-using userbase in the nix ecosystem, so you’re probably not the first to want this.

Consider making your own topic so others who have thought about this before can chime in, rather than derailing an unrelated (and solved) topic nobody not here yet will read :wink: