evil
evil copied to clipboard
Minibuffer-local post-command-hook lambdas are permanently buffer-local
Issue type
- Bug report
Environment
Emacs version: 27.1
Operating System: Linux
Evil version: cc9d6886b418389752a0591b9fcb270e83234cf9
Evil installation type: MELPA
Graphical/Terminal: X
Tested in a make emacs
session (see CONTRIBUTING.md): Tested with emacs -Q
Reproduction steps
- Start emacs -Q
- Execute the following:
(require 'package)
(package-initialize)
(require 'evil)
(evil-mode)
(minibuffer-with-setup-hook
(lambda ()
;; Install a buffer-local hook, which should stay as long as the minibuffer is alive
(add-hook 'post-command-hook (lambda () (message "HOOK %s" this-command)) nil t))
(completing-read "Test: " '(a b c)))
Press M-x, the hook is still installed and the message HOOK appears!
Expected behavior
The post-command-hook should only stay during the minibuffer session, since it is installed locally.
Actual behavior
The post-command-hook somehow stays installed (not buffer-local?)
Related issues
- Maybe issue #1261 is related?
- #1402 is probably a result of this issue
I did a bit of investigation:
- The issue only happens for lambdas, not when binding symbols
- The command hook is not put into the global post-command-hook list after closing the minibuffer, but the hook somehow reappears when opening the next minibuffer
Maybe this is of any help but I am rather clueless since I am not an evil user myself. Evil seems to be a pretty intrusive, all encompassing package. I did check if there are suspicious manipulations of the post-command-hook variable but I didn't find anything. Everywhere the list is correctly manipulated using the add-hook/remove-hook functions. But I suspect there is some other state saving/restoring going on somewhere else. Maybe it is related to the evil support in the minibuffer. This has been introduced relatively recently?
If someone is willing to investigate this - the best approach is probably git-bisecting evil.
After a cursory git bisection of the evil repo I landed upon this commit as the first commit where post-command-hook
became permenently buffer local. It seems like this commit made quite a lot of evil variables permenently local.
Specifically these lines from those commits
https://github.com/emacs-evil/evil/blob/948c49dae42adec4b81270b793caff743996bd29/evil-states.el#L36
seem to be the ones causing this. My guess is somewhere in evil their setting post-command-hook
to alias to the current state (eg. evil-normal-post-command
) post-command-hook and because it's permenent you can't revert back. Or something like that. I'll do some more investigation.
Okay, not sure if this is an emacs bug or feature but looks like if you prepend a hook with a function that has the permenent-local-hook
property then that hook also becomes permenently buffer local. Eg.
(defun foo ()
(message "Foo"))
(put 'foo 'permanent-local-hook t)
(add-hook 'post-command-hook #'foo nil t)
(describe-variable 'post-command-hook)
If you use (add-hook 'post-command-hook #'foo)
there doesn't seem to be any issue :/
Working from evil version evil-20210109.807
and commenting out all the (put var 'permanent-local-hook t)
calls from the commit above (+ one for 'evil-repeat-post-hook
in evil-repeat.el
) seems to prevent post-command-hook
becoming permenently buffer local.
@minad I can also say that I'm no longer encountering the minibuffer-errors mentioned in issue #152 when running the consult family of commands so I beleive this is definitely the cause for them.
As for an actual fix, I don't think this is it. The commit that introduced this behaviour seems to have been trying to avoid complications due to kill-all-local-variables
. I beleive we're going to need someone who understands it (maybe @TheBB) to try and help fix it. Simply commenting out all the relevent lines isn't a fix IMO.
Is there something specific to the lambdas? Why are these treated differently, such that they are automatically marked as permanently local?
Edit: Maybe the problem is that lambdas don't specify this permanent-local-hook property since they are anonymous. And then they will inherit this permanently local property from the hook variable itself?
Maybe ask on the emacs mailing list?
@minad
I'm not sure what you mean? Creating an init file init.el with the following contents:
(add-hook 'post-command-hook
(lambda ()
(message "Foo")))
(describe-variable 'post-command-hook)
Doesn't result in post-command-hook being permenent when running emacs -nw -q --load ./init.el
.
I meant that this problem only occurs for lambdas not for named functions, see my very first comment. So I wonder why lambdas are treated specially.
oh, sorry yes you're right. I'm not sure. Though my guess is probably what you suggested. It inherits it's permenently local buffer status from the hook it's inserted into.
I investigated this a bit and found out that kill-all-local-variables
, which
is responsible for clearing local hook variables, does in fact fail to remove
lambdas from hooks, marked as permanent-local-hook, because it only considers
symbols for removal.
Reported as an Emacs bug at bug#46407.
Adding a function with a permanent-local-hook
property to a hook does indeed
mark the hook symbol as permanent-local. This is, however, treated a bit
differently than normal non-hook variables: hook symbol's permanent-local
property is set to permanent-local-hook
rather than t
, which will make
kill-all-local-variables
remove all non-permanent elements from the symbol's
local value.
That is why the behavior described in
https://github.com/emacs-evil/evil/issues/1261 should be considered expected.
@jakanakaevangeli Thank you for looking into this and for reporting this upstream!
This has been fixed in 28.1 https://debbugs.gnu.org/cgi/bugreport.cgi?bug=46407.