evil icon indicating copy to clipboard operation
evil copied to clipboard

Minibuffer-local post-command-hook lambdas are permanently buffer-local

Open minad opened this issue 4 years ago • 11 comments

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

minad avatar Jan 11 '21 22:01 minad

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.

minad avatar Jan 16 '21 16:01 minad

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.

mohkale avatar Jan 17 '21 22:01 mohkale

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.

mohkale avatar Jan 17 '21 22:01 mohkale

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 :/

mohkale avatar Jan 17 '21 22:01 mohkale

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.

mohkale avatar Jan 17 '21 22:01 mohkale

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 avatar Jan 17 '21 23:01 minad

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

mohkale avatar Jan 17 '21 23:01 mohkale

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.

minad avatar Jan 17 '21 23:01 minad

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.

mohkale avatar Jan 17 '21 23:01 mohkale

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 avatar Feb 09 '21 20:02 jakanakaevangeli

@jakanakaevangeli Thank you for looking into this and for reporting this upstream!

minad avatar Feb 09 '21 20:02 minad

This has been fixed in 28.1 https://debbugs.gnu.org/cgi/bugreport.cgi?bug=46407.

minad avatar Jan 02 '23 21:01 minad