Mode-local defadvice - emacs

Is it possible to modify function's behavior with defadvice for specific mode/buffer only? I want mouse-yank-primary to insert extra text, but only when i'm in specific mode. I've tried 'defadvice after' for mouse-yank-primary, but once activated it works in all other buffer as well.
I think it can be resolved by rebinding mouse button to my own function in mode hook, but elisp manual says it's better to use defadvice.

Rebinding the mouse button to another function seems preferable to me. You don't need a mode hook for that, usually you just modify the mode's keymap:
(eval-after-load '<mode>
'(define-key <mode>-map [mouse-2] 'my-mouse-yank-primary))
Localizing an advice to a major mode is a bit harder. There's no argument you can pass to defadvice to do that, but you can set some variable's buffer-local value in a mode hook, and then check this value in the advice code. If it's set, do something special. If not, just evaluate ad-do-it.

Localizing an advice to a major mode is easy:
(defadvice foo (after bar activate)
(when (derived-mode-p 'python-mode)
(do (something) now)))

Related

Emacs ElDoc interfere Evil search

I use Emacs with Evil mode, and while I entering the search pattern, the ElDoc message is displayed replace the current search pattern. It is quite annoying. I looked into ElDoc and see a function eldoc-display-message-p that check for conditions that ElDoc should not display the message.
I need some hint to advice this function to prevent ElDoc interference with Evil search.
The function eldoc-display-message-no-interference-p is a predicate function that will determine whether or not to display an eldoc message at the time.
Searching in Evil uses isearch, so when you are searching, the variable isearch-mode will be non nil.
You can customize the behavior of eldoc-display-message-no-interference-p by editing it directly but that is often not the best choice, we can modify it's behavior instead by using "after advice". If you are unfamiliar with advice, read about it here.
(defadvice eldoc-display-message-no-interference-p (after dont-show-when-isearching activate)
"Always return nil if isearch-mode is active."
(setq ad-return-value (and ad-return-value (not isearch-mode))))

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

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)

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).

First elisp attempt - minor mode for tab key not being invoked on tab?

I've decided to get my toes wet with a bit of lisp, since I want to make emacs behave a little better when I hit TAB. My command works fine. It just performs indent-for-tab-command and if nothing happens, it performs tab-to-tab-stop, on the assumption that it's unlikely I was hitting TAB just to have the point refuse to budge when I'm inside a multi-line string or some such. After the first TAB press, it continues to do tab-to-tab-stop until either editing resumes, or the point is moved elsewhere. AFAIK, my logic is ok, though my lisp code probably isn't!
Originally I just hacked this into my emacs dot files by doing (local-set-key (kbd "TAB") 'tab-dwim) for major modes where I wanted this behaviour. That worked as expected.
Then I decided that what I was doing was basically a minor mode, so I tried to move the key-binding into a minor-mode. For some reason, even though the minor mode is enabled (as indicated in the mode line, and just from toggling it on and off), my tab-dwim function isn't being invoked when I hit the TAB key. I can still invoke it with M-x as expected.
What am I doing wrong with my minor mode's :keymap?
;;;
;; TAB DWIM
; buffer-local before/after point tracking
(setq point-before-tab nil)
(setq point-after-tab nil)
(make-local-variable 'point-before-tab)
(make-local-variable 'point-after-tab)
(defun tab-dwim ()
"Indents normally once, then switches to tab-to-tab-stop if invoked again.
tab-dwim will always perform tab-to-tab-stop if the first TAB press does not
cause the point to move."
(interactive)
(print "in tab-dwim now") ; THIS LINE IS NEVER INVOKED ON TAB?
(setq point-before-tab (point))
(if (eq point-before-tab point-after-tab) ; pressed TAB again
(tab-to-tab-stop)
(indent-for-tab-command))
(if (eq (point) point-before-tab) ; point didn't move
(tab-to-tab-stop))
(setq point-after-tab (point)))
(define-minor-mode tab-dwim-mode
"Toggle tab-dwim-mode.
With a non-nil argument, turns on tab-dwim-mode. With a nil argument, turns it
off.
When tab-dwim-mode is enabled, pressing the TAB key once will behave as normal,
but pressing it subsequent times, will continue to indent, using
tab-to-tab-stop.
If tab-dwim determines that the first TAB key press resulted in no movement of
the point, it will indent according to tab-to-tab-stop instead."
:init-value nil
:lighter " DWIM"
:keymap
'(([TAB] . tab-dwim)))
(provide 'tab-dwim)
Cheers,
Chris
I think you are very close.
Try this for your keymap:
'(("\t" . tab-dwim)))
Yes, use "\t" or the vector format "[(tab)]".
Some additional notes for your elisp development:
Avoid making global variables as much as possible. In this case, I think dynamically binding with let is appropriate. Also have a look at let*.
Understand the difference between make-local-variable and make-variable-buffer-local. The way you've written your code, the buffer-local variable would only exist in the buffer that loads your package.
As Nemo mentioned, it's extemely extremely extremely recommended that you use a common prefix for all variables/functions related to each package. Emacs has only one namespace, this is the "hacky" way to keep it somewhat organized.

Updating font-lock keywords in emacs without major mode reload

I am doing small modification to SLIME, so that I can get all currently loaded symbols from Lisp, analyze them and make font-lock fontify them.
I managed to do all these steps, but I have a small problem - when keyword list changes in font-lock the buffer is not updated unless you restart the major lisp-mode. I don't want to restart lisp-mode every time I update keywords, because I have several hooks on lisp-mode that I want to run only when I load the file for the first time.
Is there an other way to update font-lock so it reads all then new keywords and fontifies the buffer accordingly? Switching off font-lock and using font-lock-fontify-buffer does not do the trick.
UPD: Added bounty - the question is still up. I need a way to reload font-lock keyword without reloading major mode.
Ok, how about this instead:
(defun my-font-lock-restart ()
(interactive)
(setq font-lock-mode-major-mode nil)
(font-lock-fontify-buffer))
You could temporarily clear the mode hook variable and restart it:
(defun my-restart-lisp-mode ()
(interactive)
(let ((lisp-mode-hook nil))
(normal-mode)))
Triggering the major-mode is not what makes font-lock do its thing. I am not intimately familiar with the internals of SLIME or lisp-mode, but just setting the variable should make it work. Toggling font-lock-mode will make font-lock start refontifying with the new keywords in mind, as should font-lock-fontify-buffer.
I hack on cperl-mode, mostly, and it is a simple matter of cperl-init-faces (which sets the internal font-lock variables) and a restart of font-lock. lisp-mode should not be much different, except for not needing a call to cperl-init-faces ;)
Edit: some experimentation with lisp-interaction-mode reveals that even restarting font-lock-mode is not strictly necessary. Just changing font-lock-keywords is enough, as long as you re-trigger fontification somehow. (Editing text, font-lock-fontify-buffer, etc.)