pedzsanReddit · 2022-10-30 22:31
While surfing around on Emacs Stack Exchange, someone commented to go look at `dir-locals-set-directory-class`. It has a closely associated function called `dir-locals-set-class-variables`.
Base Emacs knows about projects. Projects are defined (mostly) via version control. I know about Projectile and use it as well but this is in base Emacs introduced in version 23.
The two steps are to first set up dir local variables based upon class using `dir-locals-set-class-variables`. I explain why I used `eval` later but I did this:
```lisp
(dir-locals-set-class-variables
'proj1
'((nil . ((eval . (pedz/proj1-set-default-mode-locals))))
(enh-ruby-mode . ((eval . (pedz/proj1-set-enh-ruby-mode-locals))))))
```
The second step is to tie a project (specified by its directory) to a class like this:
```lisp
(dir-locals-set-directory-class "~/Source/proj1" 'proj1)
```
And you are done. The two functions `pedz/proj1-set-default-mode-locals` and `pedz/proj1-set-enh-ruby-mode-locals` will be called based upon the mode of the file being visited within the project. And they are also called for such things as compile buffers and other buffers not visiting files within the project based upon `default-directory`. Those functions can set variables, enable modes, etc.
So... you are asking **Why not just use .dir-locals.el?**
The answer is because of *risky* variables. I now have three variables that end with `program` which are (properly) considered *risky* that need to be set in this project and it isn't possible to type `!` and tell Emacs "Yes... those variables for this directory are ok. Please don't pull on my shirt tail again about this.". Rather, each new file that is opened, it will ask if those variables are ok to be set.
And I totally get why. It is unsafe to just set those variables based upon something that may or may not be under your control. But your init files are (or should be) under your control. So, I've moved all of the project specific settings into my init files.
wunderspud7575 · 2022-10-31 07:23
My elisp knowledge isn't up to fully following what is presented here, but it seems like you're dancing around an unstated threat model associated with .dir-locals.el, involving an actor checking in malicious code to a repository? Do I have that right? Care to elaborate on the threat?
meedstrom · 2022-10-31 11:24
You mean the threat involved in setting `safe-local-variable-values` to `t`?
I don't think OP has a different threat model, just would like to skip the prompt for repos that are her own anyway.
pedzsanReddit · 2022-10-31 15:04
Lets back up for a second and review [File Variables](https://www.gnu.org/software/emacs/manual/html_node/emacs/File-Variables.html) and [Directory Variables](https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html).
I download, use, and often modify Emacs code from other repositories. These often have File Variables or Directory Variables. So when I edit a foreign file, Emacs asks me if I want to change the variables as the file or directory is asking. Usually I say "no" in this case. The files are out of my control. I can't trust them.
For files in my own repositories, I somewhat have the same problem. While I do trust the file and directory variables in my own directories, I have no way of teaching Emacs, "yes... I really do trust these" with out making changes globally which would open myself up to attacks.
In both of these cases, Emacs splits the types of variables into two classes: /safe/ and /risky/.
For /safe/ variables, Emacs pops up a question, do you trust this? You can answer "y" or "n" for a one time reply. The next time you edit the file, Emacs will ask you again.
You can also "!" which means to allow the changes and store the variables and their values away and consider them ok for the rest of time. This will stop Emacs from asking you about these same settings in the future and just automatically apply them. You can also answer "i" which says to not set them and don't ask again.
/Risky/ variables are things like paths to executables. For example, the program that Enhanced Ruby Mode spins up to do parsing is controlled by `enh-ruby-program` which defaults to "ruby". In my case, I need to set it to spin up a program within a container. Since the variable ends with `program`, Emacs considers it to be /risky/. In this case, Emacs only presents the "y" and "n" options. It does not present the "yes and remember it" option. Thus, the question pops up rather frequently.
I would encourage you to play with File and Directory variables to see the pain point.
There are global settings that can be done to avoid the question but they come at a cost of exposing exploits. For example, you can say "just trust everything" or "consider a particular variable safe", etc. None of them are prudent.
When I started down the path, I was hoping Emacs would not ask me about the variables that were being set via the directory's or project's class, but it did. I could have just put the two evals into a `.dir-locals.el` file. But the two function calls would be calling back into my init files and the functions themselves would be in my init files with no apparent users. Keeping the definitions and uses together is going to be easier for me to maintain.
Last... and a very big point: I've had a lot of situations where I needed file or directory local variables but didn't have control over the files or directories. The place I worked for 30 years had giant NFS directories of the source code that were write protected from general developers. It would have been fantastic to be able to assign classes to those directories and then assign values to Emacs variables based upon those classes. Back around 1990 I developed the concept of [Inherited Variables](https://github.com/pedz/cscope.el/blob/master/inherit.el) which "worked" but I sometimes had to fight with it because the inheritance path would get confused in rare occasions. The directories having classes concept I think is going to be more solid.
ForkInBrain · 2022-10-31 15:29
Hey, thanks for this. I was reading the info pages for directory local variables yesterday and saw this "class" thing and figured it could be set up to handle various "kinds" of directories, but I failed to notice `dir-locals-set-directory-class`.
How does this mechanism interact with `.dir-locals.el`? I ask because I wonder if it is possible to set the directory's "class" in `.dir-locals.el` and then put the rest of the config elsewhere. Also, there are projects like Emacs itself that has a `.dir-locals.el` at the root, so I wonder if those vars "win" or whether a directory class config wins.
pedzsanReddit · 2022-10-31 15:35
My **guess** is that the `.dir-locals.el` would win because they are more specific than the class. In concept, the class is a group of directories / projects. I believe I read that the File Variables win over the Directory Variables for that same reason.
The beauty of Emacs is... all the source code is right there to look at.
The other question I have is can one directory be in multiple classes.
wunderspud7575 · 2022-11-03 22:45
Wow, what a comprehensive reply, thank you so much, I learnt a bunch from that.