-
Notifications
You must be signed in to change notification settings - Fork 11
Description
Improving Emacs-Twist Usability and Documentation
Hey! I have basically never configured Emacs and I'm trying to incorporate it into my Nix workflow with twist.nix now. I'm feeling a bit overwhelmed with all the moving parts, especially since this is my first time fully properly configuring Emacs. I have several questions and suggestions that might help improve the project for newcomers. Any clarification or guidance would be really appreciated!
Note: I recognize that some of my concerns below might stem from misunderstanding how twist.nix works, or they might involve complexities/limitations in Emacs itself that are difficult or too much work to address. I'm open to being corrected on any misunderstandings!
Questions & Issues
1. IFD Dependency
It seems like IFD (Import From Derivation) is required when using twist.nix. Is this something that's here to stay, or are there plans to remove or replace this dependency in the future?
2. Home Directory Modifications via homeModule
I've noticed that the homeModule creates symlinks for init.el and early-init.el in the user's home directory. While I understand this is the default behavior, it feels suboptimal since it modifies the home directory, making the setup less reproducible and somewhat messy.
Questions:
- Is this the intended approach for configuring Emacs with twist.nix?
- Is there a more flexible approach that avoids modifying the home directory, or is this just how it works right now?
- Does twist.nix support alternative configuration methods (like wrapper-based approaches), and if so, how do I implement them?
Concern: Not everyone uses Home Manager, it is incompatible with certain systems and workflows. While Home Manager can manage the symlinks declaratively, the fundamental issue is that it pollutes the home directory, which:
- Creates impure state outside the Nix store
- Makes the setup less reproducible
- Means you can't have a purely functional configuration - there's always state in
~/.emacs.d/ - Doesn't work for users who don't use Home Manager
3. Wrapper based Module System Suggestion
Because I was having difficulty understanding how twist.nix works (there's essentially no documentation available), I ended up trying to configure Emacs in my own way outside of twist.nix entirely. I attempted to wrap the Emacs binary with flags/environment variables that point to Nix store configuration.
Questions:
- Does twist.nix already support a wrapper based approach that avoids home directory modifications? If so, could you provide documentation or examples on how to use it?
- How can I properly configure Emacs without relying on Home Manager or polluting the home directory?
Context: I found libraries like lassulus/wrappers which provide a clean, modular system for wrapping packages with flags, environment variables, and configuration files. This library makes it considerably easier to create and maintain wrapper modules through its module system.
Critical Suggestion:
The existence of the homeModule symlink approach suggests that twist.nix either doesn't have a wrapper-based solution, or the current wrapper implementation (if it exists) is suboptimal, otherwise, why would the home directory symlink approach exist in the first place?
An Emacs wrapper module system would be incredibly valuable and would provide:
- A module system to configure Emacs without Home Manager - critical for broader adoption
- No home directory pollution - no impure state in
~/.emacs.d/ - Per-user configurations - each user can have their own config without touching shared directories
- Same declarative module system benefits as
homeModule- but without the Home Manager dependency - Platform-agnostic - works across NixOS, nix-darwin, and standalone Nix installations
Implementation suggestion: If twist.nix's current wrapper system is suboptimal or non existent, consider using or taking inspiration from lassulus/wrappers. It provides:
- A proven, clean module system for creating wrappers
- Easy composition of flags, environment variables, and configuration
- Reusable wrapper modules that can be shared
- Integration with the Nix module system
A standalone wrapper implementation using this approach (or similar) would make wrapper based Emacs configuration considerably easier to implement and maintain.
However, this approach has a critical dependency: figuring out how to prevent Emacs from performing impure actions (see "Preventing Impure Emacs Behaviour" below). Without solving the impurity problem, a wrapper based approach would still generate warnings and potentially cause issues when Emacs tries to write to read only Nix store locations. This needs to be addressed as a core requirement for twist.nix, not an optional nice to have.
If wrapper based configuration already exists in twist.nix but just isn't documented, that's a major problem. This project has been around for years - even incomplete, temporary documentation would help dramatically. If it doesn't exist yet, implementing it should be a high priority since it would make twist.nix accessible to users who avoid Home Manager.
4. Creating Custom Emacs Packages
I checked out the example repositories, but they were quite complex and hard to follow as a beginner. From what I gathered, they use package definitions that aren't directly tied to homeManager.
Questions:
- Does this mean the package is implemented as a wrapped binary pointing to the Nix store configuration?
- How does the
homeModuleintegrate into this setup? - Could you provide simpler, step by step examples for creating custom packages?
5. NixOS Module for Non Home Manager Users
Suggestion: Even if you believe the homeModule symlink approach is the best solution, please consider adding a standalone NixOS module for users who don't use Home Manager. This would provide broader accessibility without forcing users to adopt Home Manager.
Two possible approaches:
a) Wrapper-based approach (preferred):
A NixOS module that creates wrapped Emacs packages per user, avoiding home directory modifications entirely while providing the same declarative module system as homeModule. This could leverage lassulus/wrappers or a similar modular wrapper system to make implementation easier. This depends on solving the impure behavior problem described in "Preventing Impure Emacs Behaviour" below.
b) Symlink based approach via tmpfiles.rules:
If you're committed to the symlink approach, at least provide a NixOS module that uses systemd.user.tmpfiles.rules (or similar) to create the symlinks per user. This would:
- Allow users who don't use Home Manager to still benefit from twist.nix
- Maintain the symlink approach if that's what you prefer
- Support per user configurations on multi user systems
- Still pollute the home directory (unfortunately), but at least make the configuration accessible without Home Manager
Example implementation concept:
programs.emacs-twist = {
users.username = {
enable = true;
# Same module options as homeModule
init.el = "...";
packages = [ ... ];
};
};This would generate tmpfiles.rules entries that create the necessary symlinks in each user's home directory, similar to how Home Manager does it, but without requiring Home Manager as a dependency.
While this still modifies the home directory (which I'm not a fan of), it would at least make twist.nix usable for the many NixOS users who don't use Home Manager.
6. Preventing Impure Emacs Behaviour (CRITICAL ISSUE)
This is the most critical issue that needs to be solved for twist.nix to truly work as a purely functional Emacs configuration system.
Emacs constantly tries to perform impure actions that fundamentally conflict with a Nix based, purely functional configuration approach. The issues include:
Problems:
- Emacs' built-in package manager (
package.el) trying to install packages - Automatic saving of customization variables to init files
- Creation of various cache and state files in
~/.emacs.d/ - Attempts to write to read-only configuration files in the Nix store
- Writing configuration files "for fun" or "just because" - Emacs assumes it can always write
- General assumption that configuration directories are writable
These behaviours happen both accidentally (when I'm not careful) and by default (Emacs' normal operation), making it difficult to maintain a pure, reproducible setup. Without solving this problem, twist.nix cannot deliver on its promise of purely functional Emacs configurations.
Proposed Solutions:
a) Automatic Configuration Injection (Highest Priority - MUST IMPLEMENT):
Twist.nix must automatically inject configuration that prevents these impure behaviors. This configuration should be loaded before the user's configuration to ensure it takes effect. For example, injected into early-init.el or as a separate config file that's loaded first:
;; Disable package.el entirely - prevent any package installation attempts
(setq package-enable-at-startup nil)
(setq package-archives nil)
;; Prevent Emacs from writing customizations to init files
(setq custom-file (make-temp-file "emacs-custom-"))
;; Redirect user-emacs-directory to a temporary location
;; This prevents Emacs from trying to write to ~/.emacs.d/ or read-only Nix store paths
(setq user-emacs-directory (make-temp-file "emacs.d-" t))
;; Disable auto-save and backup files in inappropriate locations
(setq auto-save-default nil)
(setq make-backup-files nil)
;; Prevent Emacs from creating any state files
(setq create-lockfiles nil)
;; I will be honest though, I add these to my configuration and I still see package.el creep up. It's like emacs is broken.Benefits:
- Prevents most common impure behaviors automatically
- Users don't need to remember to add this configuration themselves
- Reduces confusion and frustration for new users
- Makes the "purely functional Emacs" approach actually work out of the box
- Eliminates read only filesystem warnings
Implementation requirement: This should be mandatory and enabled by default, with an option to disable it if users really need traditional Emacs behavior (though this would break the purity model). Something like:
programs.emacs-twist = {
preventImpureBehavior = true; # Default: true, strongly discouraged to disable
};b) Binary Patching/Modification (Alternative/Additional Approach):
For an even more robust solution, consider patching the Emacs binary itself to disable problematic features at compile time:
- Completely remove
package.elfunctionality from the binary - Disable automatic custom variable saving code
- Remove code paths that create cache/state directories in configuration locations
- Patch out warnings/errors when attempting to write to read only directories
- Strip out any "helpful" file creation behaviors
This would prevent impure behavior at the source rather than trying to work around it with Elisp configuration. This is a more aggressive approach but would guarantee purity.
c) Wrapper Environment Configuration:
The wrapper could set environment variables and command line flags that prevent write attempts:
- Override
HOMEfor the Emacs process to a temporary directory - Set Emacs specific environment variables to redirect state/cache directories to an impure location if absolutely necessary
- Provide command line flags that disable problematic features
This works in combination with pointing Emacs to the actual read-only configuration in the Nix store for reading.
d) Clear Documentation:
At minimum, provide comprehensive documentation on:
- Why these impure behaviors are fundamentally incompatible with Nix
- How twist.nix prevents these behaviors automatically
- What to do if users encounter write related warnings (though they shouldn't with proper prevention)
- Best practices for maintaining a pure Emacs configuration
- The trade offs of disabling the automatic impurity prevention (if that option exists)
Why This Must Be Solved:
This is THE biggest barrier to using Emacs with Nix successfully. Without addressing these impure behaviors:
- Users will constantly fight against Emacs' assumptions about file system mutability
- Read only filesystem warnings will plague the user experience
- Twist.nix cannot claim to provide truly pure, reproducible configurations
- The entire value proposition of using Nix for Emacs configuration is undermined
Twist.nix should make the purely functional approach the default and only supported path. This isn't optional - it's a fundamental requirement for the project's stated goals. If users want impure, traditional Emacs behaviour, they shouldn't be using twist.nix in the first place.
7. Documentation & Package Management
As someone new to Emacs and kind of a noob in Nix, I'm struggling with configuring Emacs with twist.nix. There's essentially no documentation available on:
- How to manage individual emacs plugins with flakes/npins/niv
- How to use the emacs wrapper (if it exists) to make a package definition in my configuration
- Basic workflow and architecture
What would help:
Even incomplete, temporary documentation would be dramatically better than nothing. This project has been around for years - some basic guidance would go a long way:
- Clearer documentation or examples on how to manage Emacs packages with these tools
- Step by step guides covering typical use cases for setting up Emacs within a Nix environment
- Clear explanation of available configuration approaches: What are the different ways to configure Emacs with twist.nix? What are the pros/cons of each? (Especially: wrapper based vs home directory symlinks)
- Documentation on how impurity prevention works and why it's necessary
- Documentation on how to tie the various pieces together like the
homeModule, wrapped binaries, and Nix store configurations - Examples that start simple and gradually increase in complexity, rather than jumping straight to advanced configurations
- A "getting started" guide specifically for Emacs beginners coming from other editors
- Better code documentation or architectural overview to help users understand the source code if they need to dig deeper
I understand documentation is time consuming, but even a rough, incomplete guide would help tremendously. A simple README with basic examples, common pitfalls, and a high-level architecture explanation would be infinitely better than the current situation.
Summary
Overall, twist.nix looks really promising, but the learning curve is quite steep for newcomers to both Emacs and Nix. The complete lack of documentation and the difficulty in following the source code led me to try re-implementing my Emacs config outside of twist.nix entirely, which defeats the purpose of using the library!
Key pain points:
- CRITICAL: Complete lack of documentation - This project has been around for years, even basic/incomplete docs would help dramatically
- CRITICAL: Emacs' impure behaviours (package.el, auto saving customisation, arbitrary file writes) are not prevented, causing constant friction and undermining the entire Nix based approach
- Unclear what configuration approaches are available and how to use them
- The home directory symlink approach creates impure state even though Home Manager manages them declaratively (suggests wrapper approach doesn't exist or is suboptimal)
- Source code is hard to follow for me at least 😐
- Examples configurations are too complex and lack progressive learning paths
- Home Manager dependency is a significant barrier for users who don't want or need it
Proposed Solutions (in priority order):
- MUST HAVE: Basic documentation - Even incomplete, temporary docs explaining basic usage, architecture, and common patterns
- MUST SOLVE: Automatic prevention of impure Emacs behaviours - This is non negotiable and must be implemented for twist.nix to work as intended
- Wrapper based module system that avoids home directory modifications entirely (could leverage
lassulus/wrappersor similar for easier implementation) - NixOS module using tmpfiles.rules for users without Home Manager (if symlink approach is preferred)
- Simpler, progressive examples
Without basic documentation and solving the impurity problem, the fundamental user experience will remain broken.
I'm happy to help test solutions or contribute to documentation once I better understand the system!
Thanks for your work on this project!