2

I have a project with a .dir-locals.el file. This file contains some customizations for various project local variables, e.g. include directories for semantic and arguments for clang.

First time opening the project, Emacs asks if I want to consider these variables safe. Obviously I press !, because I don't want to do this every time I open up Emacs.

The problem is that my init.el is part of a git repository, and these local customizations are saved inside the init file as such:

(custom-set-variables
 ;; custom-set-variables was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 [...]
 '(safe-local-variable-values
   '((eval semantic-add-system-include "custom/include/path")
   [...]
   ))

But, IMO., these customizations are meant to be local, ie. not included in the git worktree. And I would really like to not having to include them in the init file.

Could I perhaps point to these safe-local-variable-values by storing them inside another elisp file, and then .gitignore that file instead? And how could I achieve this? Or is there a better way?

Edit:

There's a related answer here, but that moves ALL customizations inside another file, whereas I would like to filter ONLY on the safe-local-variable-values (hoping that it's actually possible).

1
  • Include directories and clang arguments may be set locally in some flycheck variables - e.g. flycheck-clang-include-path and flycheck-clang-args, avoiding .dir-locals.el. Some other(s) may be set with lsp-mode.
    – Ian
    Commented Sep 8, 2023 at 12:20

2 Answers 2

2

Yes, possible. You need to change the definition of hack-local-variables-confirm. Find the form (customize-push-and-save (if (= char ?!) ...)) and modify it.

I agree with you to some extent: these should be put into another file. But to be honest, I don’t recommend you change the function’s definition, because this is not elegant, so maybe we'll wait and see if there's any other way.


BTW, you can take a look at my code. (Please ignore the coding style; I will refactor it someday but not now.) try the code shown below. It doesn't answer your question (or, you could say that it answers, because the files that you trust are where the safe variables are.), but does solve your problem: you just need to put the .dir-locals.el file’s path to your .emacs, then Emacs will never ask you to confirm, because these variables are stored in safe-local-variable-values and thus considered safe:

(let ((shynur/safe-local-variable-values (list)))
  (named-let get-vars ((dir-locals
                        (mapcan (lambda (file-path)
                                  (when (file-exists-p file-path)
                                    (with-temp-buffer
                                      (insert-file-contents file-path)
                                      (read (current-buffer)))))
                                ["~/.emacs.d/.dir-locals.el"
                                 "~/.emacs.d/.dir-locals-2.el"
                                 ;; Files that you trust ...
                                 ])))
    (dolist (mode-vars dir-locals)
      (let ((vars (cdr mode-vars)))
        (if (stringp (car mode-vars))
            (get-vars vars)
          (dolist (var-pair vars)
            (push var-pair shynur/safe-local-variable-values))))))
  (setq safe-local-variable-values (nconc shynur/safe-local-variable-values
                                          safe-local-variable-values)))
1
  • Thank you for the suggestion, though I was hoping for something a bit more optimal - the way you have set it up is such that the name and path of local .dir-locals files are included in repository, which I kind of wanted to avoid, especially if/when open-sourcing the init file. Or I end up with a global .dir-locals file, which means that all my C++ projects locally will have all the same (unnecessary include directories). But I can explore with having another file (ignored by git), which contains a list of .dir-locals files and use that in the init. Gotta experiment! Thanks again.
    – alexpanter
    Commented Jun 29, 2023 at 9:36
2

Since you said your skills with elisp are very limited, I tried to simplify the solution. Now all you need to do is change the value of your/safe-vars-file.

;;; Put them into your configuration file.

(defconst your/safe-vars-file "C:/Users/shynur/.emacs.d/my-safe-vars.el"
  "The file to store safe local variables.")

(unless (file-exists-p your/safe-vars-file)
  (with-temp-file your/safe-vars-file))

(advice-add 'hack-local-variables-confirm :around
            (lambda (fn &rest args)
              (let ((custom-file your/safe-vars-file))
                (apply fn args))))

(when-let ((your/safe-vars
            (with-temp-buffer
              (insert-file your/safe-vars-file)
              (ignore-errors
                (cadadr (cadar (seq-filter
                                (lambda (quoted-var-list)
                                  (eq (caadr quoted-var-list) 'safe-local-variable-values))
                                (cdr (read (current-buffer))))))))))
  (setq safe-local-variable-values your/safe-vars))
2
  • Hi @shynur, thank you for the suggestion. But my skills with elisp are very limited.. could you perhaps add some hints to what my/read-safe-vars should do or how it should be structured? It would actually be a pretty big help!
    – alexpanter
    Commented Sep 7, 2023 at 12:34
  • @alexpanter: Please try my new code.
    – shynur
    Commented Sep 8, 2023 at 10:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.