5

Seems like most major modes clear all local variables before initializing. It also seems like many major modes read variables while initializing to customize their behavior.

If I want to set a file or directory local variable to determine major mode behavior (for example, overwriting web mode's web-mode-engines-alist in .dir-locals.el), how can it be done so that the local variables are written before the major mode actually initializes, but are not cleared?

One way to do it would be to bind/rebind global variables using ((nil . ((eval . ((lambda () (setq name val))))))) in .dir-locals.el. The nil key seems to run before any major-modes are entered. Still, it doesn't seem ideal to overwrite global config. For example, maybe I want to work on multiple projects in the same emacs instance.

sfridman
  • 151
  • 4
  • 1
    Some related reading: http://stackoverflow.com/q/19280851, http://stackoverflow.com/q/5147060 – phils Aug 15 '16 at 01:15
  • 1
    I did see those. Very thorough answers, thank you! It seems though that both the problems of reloading dir-local variables and of accessing them in major mode hooks are slightly different than mine. In all solutions the local variables are made available strictly after the major mode has initialized (ie, run the body given in `define-derived-mode`) – sfridman Aug 16 '16 at 09:49
  • 1
    I'm new to Emacs btw. I'm wondering now though, if the correct answer is that _if_ major mode authors want to allow users to customize this phase, they need to write the code such that the execution of the major mode body is somehow deferred until after the local variables have been loaded? – sfridman Aug 16 '16 at 09:51

2 Answers2

3

If you want to set a buffer-local value for a given mode, do so after the mode has been established. You do that by putting the value-assignment on the mode hook.

For example, if you want to set local variable foo to 42 in lisp-mode then do something like this:

(add-hook 'lisp-mode (lambda () (setq-local fill-paragraph-function  'my-fill-para)))

Or better yet, since it facilitates programmatic access, including use of remove-hook, use a named function:

(defun my-set-lisp-fill () (setq-local fill-paragraph-function  'my-fill-para))

(add-hook 'lisp-mode 'my-set-lisp-fill)
Drew
  • 75,699
  • 9
  • 109
  • 225
  • @phils: Right you are; updated. – Drew Aug 15 '16 at 01:15
  • 1
    Thanks for the answer! Unfortunately it seems like, since buffer locals are also killed when switching major modes, this solution has the same problem as file and dir local variables in that they cannot be accessed in the initialization stage of a major mode – sfridman Aug 16 '16 at 09:26
  • 1
    I've edited the initial question to be more specific – sfridman Aug 16 '16 at 09:42
1

Try something like this: (make the variable permanent-local and call web-mode after setting the variables)

((web-mode . ((eval . (progn
                        (setq-local web-mode-engines-alist '(("django" . "\\.html\\'")))
                        (put 'web-mode-engines-alist 'permanent-local t)
                        (web-mode))))))
xuhdev
  • 1,839
  • 13
  • 26
  • 1. You're setting the global value of `web-mode-engines-alist`. 2. If you made it buffer-local first, calling `web-mode` would delete the buffer-local value before anything else happened, unless you also give it the `permanent-local` symbol property. – phils Sep 28 '16 at 19:29
  • @phils Updated! Thanks for your comments! – xuhdev Sep 28 '16 at 23:28