How to keep dir-local variables when switching major modes? - emacs

I'm committing to a project where standard indentations and tabs are 3-chars wide, and it's using a mix of HTML, PHP, and JavaScript. Since I use Emacs for everything, and only want the 3-char indentation for this project, I set up a ".dir-locals.el" file at the root of the project to apply to all files/all modes under it:
; Match projets's default indent of 3 spaces per level- and don't add tabs
(
(nil .
(
(tab-width . 3)
(c-basic-offset . 3)
(indent-tabs-mode . nil)
))
)
Which works fine when I first open a file. The problem happens when switching major modes- for example to work on a chunk of literal HTML inside of a PHP file. Then I lose all the dir-local variables.
I've also tried explicitly stating all of the modes I use in ".dir-locals.el", and adding to my .emacs file "dir-locals-set-class-variables / dir-locals-set-directory-class". I'm glad to say they all behave consistently, initially setting the dir-local variables, and then losing them as I switch the major mode.
I'm using GNU Emacs 24.3.1.
What's an elegant way of reloading dir-local variables upon switching a buffer's major-mode?
-- edit -- Thanks for the excellent answers and commentary both Aaron and phils! After posting here, I thought it "smelled" like a bug, so entered a report to GNU- will send them a reference to these discussions.

As per comments to Aaron Miller's answer, here is an overview of what happens when a mode function is called (with an explanation of derived modes); how calling a mode manually differs from Emacs calling it automatically; and where after-change-major-mode-hook and hack-local-variables fit into this, in the context of the following suggested code:
(add-hook 'after-change-major-mode-hook 'hack-local-variables)
After visiting a file, Emacs calls normal-mode which "establishes the proper major mode and buffer-local variable bindings" for the buffer. It does this by first calling set-auto-mode, and immediately afterwards calling hack-local-variables, which determines all the directory-local and file-local variables for the buffer, and sets their values accordingly.
For details of how set-auto-mode chooses the mode to call, see C-hig (elisp) Auto Major Mode RET. It actually involves some early local-variable interaction (it needs to check for a mode variable, so there's a specific look-up for that which happens before the mode is set), but the 'proper' local variable processing happens afterwards.
When the selected mode function is actually called, there's a clever sequence of events which is worth detailing. This requires us to understand a little about "derived modes" and "delayed mode hooks"...
Derived modes, and mode hooks
The majority of major modes are defined with the macro define-derived-mode. (Of course there's nothing stopping you from simply writing (defun foo-mode ...) and doing whatever you want; but if you want to ensure that your major mode plays nicely with the rest of Emacs, you'll use the standard macros.)
When you define a derived mode, you must specify the parent mode which it derives from. If the mode has no logical parent, you still use this macro to define it (in order to get all the standard benefits), and you simply specify nil for the parent. Alternatively you could specify fundamental-mode as the parent, as the effect is much the same as for nil, as we shall see momentarily.
define-derived-mode then defines the mode function for you using a standard template, and the very first thing that happens when the mode function is called is:
(delay-mode-hooks
(PARENT-MODE)
,#body
...)
or if no parent is set:
(delay-mode-hooks
(kill-all-local-variables)
,#body
...)
As fundamental-mode itself calls (kill-all-local-variables) and then immediately returns when called in this situation, the effect of specifying it as the parent is equivalent to if the parent were nil.
Note that kill-all-local-variables runs change-major-mode-hook before doing anything else, so that will be the first hook which is run during this whole sequence (and it happens while the previous major mode is still active, before any of the code for the new mode has been evaluated).
So that's the first thing that happens. The very last thing that the mode function does is to call (run-mode-hooks MODE-HOOK) for its own MODE-HOOK variable (this variable name is literally the mode function's symbol name with a -hook suffix).
So if we consider a mode named child-mode which is derived from parent-mode which is derived from grandparent-mode, the whole chain of events when we call (child-mode) looks something like this:
(delay-mode-hooks
(delay-mode-hooks
(delay-mode-hooks
(kill-all-local-variables) ;; runs change-major-mode-hook
,#grandparent-body)
(run-mode-hooks 'grandparent-mode-hook)
,#parent-body)
(run-mode-hooks 'parent-mode-hook)
,#child-body)
(run-mode-hooks 'child-mode-hook)
What does delay-mode-hooks do? It simply binds the variable delay-mode-hooks, which is checked by run-mode-hooks. When this variable is non-nil, run-mode-hooks just pushes its argument onto a list of hooks to be run at some future time, and returns immediately.
Only when delay-mode-hooks is nil will run-mode-hooks actually run the hooks. In the above example, this is not until (run-mode-hooks 'child-mode-hook) is called.
For the general case of (run-mode-hooks HOOKS), the following hooks run in sequence:
change-major-mode-after-body-hook
delayed-mode-hooks (in the sequence in which they would otherwise have run)
HOOKS (being the argument to run-mode-hooks)
after-change-major-mode-hook
So when we call (child-mode), the full sequence is:
(run-hooks 'change-major-mode-hook) ;; actually the first thing done by
(kill-all-local-variables) ;; <-- this function
,#grandparent-body
,#parent-body
,#child-body
(run-hooks 'change-major-mode-after-body-hook)
(run-hooks 'grandparent-mode-hook)
(run-hooks 'parent-mode-hook)
(run-hooks 'child-mode-hook)
(run-hooks 'after-change-major-mode-hook)
Back to local variables...
Which brings us back to after-change-major-mode-hook and using it to call hack-local-variables:
(add-hook 'after-change-major-mode-hook 'hack-local-variables)
We can now see clearly that if we do this, there are two possible sequences of note:
We manually change to foo-mode:
(foo-mode)
=> (kill-all-local-variables)
=> [...]
=> (run-hooks 'after-change-major-mode-hook)
=> (hack-local-variables)
We visit a file for which foo-mode is the automatic choice:
(normal-mode)
=> (set-auto-mode)
=> (foo-mode)
=> (kill-all-local-variables)
=> [...]
=> (run-hooks 'after-change-major-mode-hook)
=> (hack-local-variables)
=> (hack-local-variables)
Is it a problem that hack-local-variables runs twice? Maybe, maybe not. At minimum it's slightly inefficient, but that's probably not a significant concern for most people. For me, the main thing is that I wouldn't want to rely upon this arrangement always being fine in all situations, as it's certainly not the expected behaviour.
(Personally I do actually cause this to happen in certain specific cases, and it works just fine; but of course those cases are easily tested -- whereas doing this as standard means that all cases are affected, and testing is impractical.)
So I would propose a small tweak to the technique, so that our additional call to the function does not happen if normal-mode is executing:
(defvar my-hack-local-variables-after-major-mode-change t
"Whether to process local variables after a major mode change.
Disabled by advice if the mode change is triggered by `normal-mode',
as local variables are processed automatically in that instance.")
(defadvice normal-mode (around my-do-not-hack-local-variables-twice)
"Prevents `after-change-major-mode-hook' from processing local variables.
See `my-after-change-major-mode-hack-local-variables'."
(let ((my-hack-local-variables-after-major-mode-change nil))
ad-do-it))
(ad-activate 'normal-mode)
(add-hook 'after-change-major-mode-hook
'my-after-change-major-mode-hack-local-variables)
(defun my-after-change-major-mode-hack-local-variables ()
"Callback function for `after-change-major-mode-hook'."
(when my-hack-local-variables-after-major-mode-change
(hack-local-variables)))
Disadvantages to this?
The major one is that you can no longer change the mode of a buffer which sets its major mode using a local variable. Or rather, it will be changed back immediately as a result of the local variable processing.
That's not impossible to overcome, but I'm going to call it out of scope for the moment :)

Be warned that I have not tried this, so it may produce undesired results ranging from your dir-local variables not being applied, to Emacs attempting to strangle your cat; by any sensible definition of how Emacs should behave, this is almost certainly cheating. On the other hand, it's all in the standard library, so it can't be that much of a sin. (I hope.)
Evaluate the following:
(add-hook 'after-change-major-mode-hook
'hack-dir-local-variables-non-file-buffer)
From then on, when you change major modes, dir-local variables should (I think) be reapplied immediately after the change.
If it doesn't work or you don't like it, you can undo it without restarting Emacs by replacing 'add-hook' with 'remove-hook' and evaluating the form again.

My take on this:
(add-hook 'after-change-major-mode-hook #'hack-local-variables)
and either
(defun my-normal-mode-advice
(function &rest ...)
(let ((after-change-major-mode-hook
(remq #'hack-local-variables after-change-major-mode-hook)))
(apply function ...)))
if you can live with the annoying
Making after-change-major-mode-hook buffer-local while locally let-bound!
message or
(defun my-normal-mode-advice
(function &rest ...)
(remove-hook 'after-change-major-mode-hook #'hack-local-variables)
(unwind-protect
(apply function ...)
(add-hook 'after-change-major-mode-hook #'hack-local-variables)))
otherwise and finally
(advice-add #'normal-mode :around #'my-normal-mode-advice)

Related

When to use add-hook, eval-after-load in Emacs

I'v read eval-after-load vs add-hook. According to the page,
code in eval-after-load is executed once.
On the other hand, code inside add-hook will run every time a buffer of that mode in opened.
Ok. But How can I figure out which code should be inside eval-after-load block or add-hook block? for example,
(eval-after-load "js2-mode"
'(progn
(js2-basic-offset 2)))
or
(eval-after-load "js2-mode"
'(progn
(defun custom:js2-config ()
(js2-basic-offset 2))
(add-hook 'js2-mode-hook 'custom:js2-config)))
I'm not asking about js2-basic-offset.
Is there a general rule?
or.. whenever I use eval-after-load, I have to ask?
Are you setting a global variable or a local variable?
If you're setting a buffer-local variable (one whose value may be different in different buffers), it has to be done after the file is loaded, so in the major mode hook. The same thing goes if you're calling a function which has an effect on the current buffer.
If you're setting a global variable, it doesn't make sense to do that in a mode hook: you'd be changing a global variable whenever you happen to load a file in a certain mode. The same thing goes if you're calling a function which affects global settings.
Many global variables can be set directly in your .emacs, without waiting for the library that will declare and use it to be loaded. In fact, this is usually the case for global variables.
js2-basic-offset is a variable, not a function. If you want to configure the indentation in all JS buffers, set it directly in your .emacs, either with
(setq js2-basic-offset 2)
or through the Customize interface.
If you want to set the value differently in different files, then you would set it in the major mode hook — something like
(defun custom:js2-config ()
(when (…)
(make-local-variable 'js2-basic-offset)
(setq js2-basic-offset 2)))
(add-hook 'js2-mode-hook 'custom:js2-config)
You don't need to wait for the library to be loaded to add a hook.
Note that Emacs has built-in facilities that cover typical use cases of setting a variable on a per-file or per-project basis: file-local variables and directory-local variables.

How to make emacs switch to *scheme* by default when eval / load from the file?

The problem:
I'm in scheme-mode (simple quack)
scheme is already running
eval expression
it sends the expression to scheme buffer
but! doesn't bring that buffer up in 2nd window = no immediate feedback
and I have to manually switch second buffer to scheme which is annoying
Some (more recent) modes like fsharp-mode or tuareg do that automatically. Tried to read quack.el, but didn't find convenient separate function like "pop scheme". It's tangled within run-scheme which also changes focus. Settings also don't help.
I want to stay in my rkt/scm file and see repl buffer pop up if not already popped. Like this simple build logic in sublime-text but with persistent repl.
Maybe I should try geiser, but quack is ok for now. Just missing few obvious conveniences.
Just rungeiser. It's in MELPA now, so it's a quick install.
You can also try lispy (which uses geiser) for in-place scheme eval.
e will eval current expression and display the result in the minibuffer.
E will eval current expression and insert the result in current buffer.
u is bound to undo, so you can either e or Eu if you prefer.
I ended up writing this:
(setq scheme-program-name "guile")
(defun run-scheme-2.0 ()
"Loads your chosen Scheme implementation for interactive development,
and displays that buffer below your main editing buffer, and makes sure that
your cursor will be on your code."
(interactive)
(if (not (get-buffer "*scheme*"))
(progn
(setq starting-buffer (buffer-name))
(run-scheme scheme-program-name)
(split-window-below)
(other-window 1)
(shrink-window-if-larger-than-buffer)
(other-window 1)
(switch-to-buffer starting-buffer))))
(add-hook 'scheme-mode-hook 'run-scheme-2.0)

How to use a minibuffer-exit-hook with read-string

I have not been able to get the minibuffer-exit-hook to play nice with read-string. As far as I can tell, I should no longer be in the minibuffer after finishing up with read-string. However, the condition (minibufferp) says I'm still in the minibuffer even though read-string finished. read-string is written in C, so I can't add the hook there (i.e., at the tail end of the read-string function).
"Documentation [minibuffer-exit-hook]:  Normal hook run just after exit from minibuffer.
[After thinking a little more about this, I'm pretty sure it's a bug -- so I filed a bug report: bug#16524. As I learn more, I'll update this thread.
(defun test ()
(interactive)
(read-string "Prompt: " "testing"))
(add-hook 'minibuffer-exit-hook (lambda ()
(cond
((minibufferp)
(message "Focus is still in the minibuffer: %s" (buffer-name)))
(t (message "Contragulations -- focus is now in: %s." (buffer-name))))))
The doc string is not exact; that's all. The hook is run when inputting text in the minibuffer is done (no longer possible). The buffer that is current when it is run is still the minibuffer. (And that's the way it should be, FWIW.)
Note that the Elisp manual puts it slightly differently (but again, not very precisely):
This is a normal hook that is run whenever the minibuffer is
entered.
("Whenever", meaning about the same time as, not necessarily after.)
If you want to do something after every use of read-string in your code, then define a function that does the following: first (read-string...), then whatever you want done next. And use that function.
If you need to affect also other invocations of read-string, besides those you write in your code, then advise function read-string to perform whatever action after the vanilla code finishes.
For example:
(defadvice read-string (after fooness activate)
(message "buffer: %S" (current-buffer)))
[Note: Yes, you can advise primitives (functions written in C). You used to even be able to advise special forms, but they regressively took away that feature.]
Running a hook after you truly exited the minibuffer is rather pointless: you could be in any kind of buffer (since minibuffer use can be triggered from anywhere) and you hence know very little about the current context (unless you use a buffer-local exit-hook, I guess).
If you want to run a hook when the selected window changes, then your best option is probably to use a post-command-hook that stores the current selected-window in an auxiliary variable and uses it to compare to the previous selected-window.

How to remove "Toggling multi-web-mode off; better pass an explicit argument" message in Emacs?

I have noticed, from a while, that every time I run emacs in multi-web-mode I get the following message:"Toggling multi-web-mode off; better pass an explicit argument".
Why do I get that? multi-web-mode is not toggled off because I use it and it works fine, but I keep getting this message all the time.
How can I fix this little trouble?
This is my code in my .emacs file where "multi-web-mode" appears (I am using Emacs 23.4):
(require 'multi-web-mode)
(setq mweb-default-major-mode 'html-mode)
(setq mweb-tags '((c++-mode "<\\?php\\|<\\? \\|<\\?=" "\\?>")
(ecmascript-mode "<script +\\(type=\"text/javascript\"\\|language=\"javascript\"\\)[^>]*>" "</script>")
(css-mode "<style +type=\"text/css\"[^>]*>" "</style>")))
(setq mweb-filename-extensions '("php" "htm" "html" "ctp" "phtml" "php4" "php5"))
(multi-web-global-mode 1)
(add-to-list 'auto-mode-alist '("\\.php\\'" . multi-web-mode))
(add-to-list 'auto-mode-alist '("\\.html\\'" . multi-web-mode))
Well it sounds like somewhere there's some code causing this mode to be called, non-interactively, with no arguments: (multi-web-mode). A common cause would be specifying a mode function symbol for use as a callback, rather than specifying a function which will call that mode function (with an argument).
This certainly used to be bad form, as it would indeed act as a toggle, just as it does in the interactive case (e.g. M-x multi-web-mode). Since Emacs 24, however, it is actually safe to do (at least for modes defined with the standard macros), as a non-interactive call with no argument to a mode function now always means 'enable'. This might account for the fact that you're not actually seeing a problem.
I don't think stock Emacs has ever generated such a message, so I would presume it's multi-web-mode itself which is detecting and reporting on this. It may need to be updated to reflect the change in Emacs 24. (Edit: looks like I was wrong about that; I guess Emacs 23.4 did produce this warning.)
Your simplest fix is probably to show us any lines from your config which mention multi-web-mode, so someone can show you how to change it to ensure that it passes an explicit argument (which is typically 1 to enable a mode).
Edit: Okay, your problem is that multi-web-mode is a minor mode (as opposed to a major mode), but you have specified it in auto-mode-alist which is a mechanism for mapping filename patterns to major modes.
So the cause is exactly as I suggested above when I said "A common cause would be specifying a mode function symbol for use as a callback", as those auto-mode-alist entries will result in a call to (multi-web-mode).
It appears to me that multi-web-global-mode handles enabling the minor mode as required, so you just need to remove the bad auto-mode-alist entries.

how to change buffer-local variable for a major mode in Emacs?

Generally, how can I customize the value of a buffer-local variable in Emacs? For example, the variable w3m-lnum-mode is buffer-local, if I set (setq w3m-lnum-mode t) in .emacs, its value in a w3m mode buffer is still nil. How could I set it to t in w3m major mode?
Major modes have a hook variable for this sort of thing. Look for w3m-mode-hook.
(defun my-w3m-hook nil
(setq w3m-lnum-mode t))
(add-hook 'w3m-mode-hook #'my-w3m-hook)
The indirection to hook a separate function is not absolutely necessary, but simplifies the management of the hook functionality (otherwise you'd have to restart Emacs or jump through several hoops to add something to an existing hook; now all you need to do is evaluate a new defun of the function called from the hook).
You can set a default like so:
(setq-default w3m-lnum-mode t)
For fine-grained control, use a hook as RNAer suggests. As far as I can tell though, this is not a normal local variable but a minor mode variable. You actually probably want to do (w3m-lnum-mode 1).