reddit / emacs
Announcing "dir-local-env.el", set directory-local variables without a ".dir-locals.el" file (for example from within "init.el")
ramin-honary-xc · 2022-03-06 07:52
Announcing [dir-local-env.el](https://github.com/RaminHAL9001/dir-local-env ), a method of setting directory-local variables from within `init.el` rather than creating a file called `.dir-locals.el` in each project directory.
This is a package my alter-ego has been working on for a few months now. It is a package not quite ready for a full release yet, but I am announcing it here hoping to get some feedback.
- Would something like this be useful to you?
- Is there already a better way to do what my implementation here is doing?
- If you think it could be useful to you, does it seem intuitive to use?
- What features do you wish it had, what could be improved?
- If you are kind enough to do a code review, have I made any obvious mistakes? (I am still new to Emacs Lisp).
## What it does
- You register directories as having directory local environments using `M-x dir-local-env-register`.
- You can register directory local environments with the `defdir-local-env` macro, which has syntax similar to the `(let* (...))` macro.
- (EDIT) Unlike the built-in `dir-locals-set-class-variables` function, you don't need to first declare a "class" of variables and then apply that class. `defdir-local-env` applies a unique set of variables to each directory without needing to define a "class", and individual variables can be updated and applied (mostly) immediately (with a few caveats).
- Provides a global minor mode `dir-local-env-global-minor-mode` which makes registered directories recognized by the `project.el` APIs as project directories.
- The `.dir-locals.el` file overrides the variables set by the `defdir-local-env` macro, so this package complements the existing directory locals mechanism, it doesn't replace it.
- The minor mode also sets advice on `make-process` and friends, so if the `process-environment` variable and/or `exec-path` variable has been set, all processes launched from within an Emacs buffer for a file contained within the directory can inherit this environment. This makes it behave somewhat similar to [direnv](https://direnv.net ) but is implemented purely in Emacs Lisp.
- Directory local environments can be enabled and disabled quickly by changing the `disable-dir-local-env` variable to `nil` or `t`.
- The `M-x dir-local-env-show-all` command shows a listing of all directory local variables for a the current buffer.
7890yuiop · 2022-03-06 08:20
> Is there already a better way to do what my implementation here is doing?
I haven't looked at your code but, as documented in the manual, you don't need any additional modes or libraries in order to "set directory-local variables without a ".dir-locals.el" file (for example from within "init.el")". Emacs provides for this OOTB.
Another method of specifying directory-local variables is to define a
group of variables/value pairs in a “directory class”, using the
‘dir-locals-set-class-variables’ function; then, tell Emacs which
directories correspond to the class by using the
‘dir-locals-set-directory-class’ function. These function calls
normally go in your initialization file (*note Init File::). This
method is useful when you can’t put ‘.dir-locals.el’ in a directory for
some reason. For example, you could apply settings to an unwritable
directory this way:
(dir-locals-set-class-variables 'unwritable-directory
'((nil . ((some-useful-setting . value)))))
(dir-locals-set-directory-class
"/usr/include/" 'unwritable-directory)
ramin-honary-xc · 2022-03-06 09:25
Thanks for mentioning this. Actually I was already aware of this mechanism, but I should perhaps write into the README file how this package differs from that. Basically, the issue I had with the `dir-locals-set-class-variables` was:
1. I had to create a new directory class for every single directory, which means interning a different symbol for each directory, and
2. Changing the environment variables was always a 2-step process, first set the directory class, then apply it to the directory.
3. I could not find an easy way to set a project directory for the `project.el` APIs using the `dir-locals-set-class-variables` mechanism.
With `dir-local-env`, there is no need to first declare a directory class, you simply set the directory local variables on a directory directly, and the changes are applied mostly immediately -- although you do need to call `hack-dir-local-variables` again on each buffer to get the updates, I still need to automate this.
7890yuiop · 2022-03-06 10:15
> I had to create a new directory class for every single directory
Your use-case must be very different to mine. I have a class for each *type* of project, and for every project of a given type I assign that single common class to the project directory.
ramin-honary-xc · 2022-03-06 10:45
> Your use-case must be very different to mine.
Indeed it is, I am caching the `process-environment` and `exec-path` variables on a per-directory basis, as these values are computed by the [Nix](https://nixos.org ) functional package manager for each project I work on.
There are other solutions for working with Nix environments, but these involved computing the environment and storing it in a `.dir-locals.el` file in the package database and symbolic linking it to the project directory, or computing the entire environment using `nix-shell` in a **synchronous** process (which can freeze Emacs for many minutes).
The [direnv](https://direnv.net) project in conjuntion with the Nix package manager and a third-party utility to transmit the computed Nix environment to Emacs via `direnv` was another option, but I felt the functionality of `direnv` in Emacs would be better implemented in Emacs Lisp.
7890yuiop · 2022-03-06 10:50
Yikes. Ok, fair enough then.
astoff1 · 2022-03-06 19:57
If you are interested in a “direnv but in Elisp”, you may want to check out this package: https://github.com/astoff/buffer-env.
It uses `.envrc` files by default, but you can cook up a tighter Nix integration along these lines: https://www.reddit.com/r/emacs/comments/t599l6/comment/hzfvu4f/?utm_source=share&utm_medium=web2x&context=3. (I'll be happy to add some directions to the README if you figure it out.
ramin-honary-xc · 2022-03-06 21:04
Thanks! I actually just ran across your package a few days ago while looking for prior art! I downloaded it, but haven't had time to read through your code yet. I was interested to see the techniques you used to extract the environment, because this is definitely a feature I want to include in my `dir-local-env` system. If `buffer-env` works well, I may use it as a dependency for my own project.
I have had a hell of a time getting the process filters and process sentinels to work, they don't seem to do what the Elisp documentation says they do, I think the Elisp documentation might be out of date.
7890yuiop · 2022-03-06 23:51
`M-x report-emacs-bug` if there's a documentation error.
And just in case: Be sure to read the manuals via `M-x info` to ensure you're reading the docs for the same version of Emacs you're using.
ideasman_42 · 2022-03-07 06:41
I also wanted to avoid having to add files into my projects repositories, however I also didn't especially want to have to configure my projects in my `init.el` file either, so I wrote [sidecar-locals](https://gitlab.com/ideasman42/emacs-sidecar-locals) which allows something similar to dir-locals that can be located above the projects in a sub-directory.
ramin-honary-xc · 2022-03-07 12:04
Thanks for sharing your work! It is pretty similar to my idea, I'll be sure mention it in my README file when I write the "Prior Art" section.