Skip to main content
Incorporated Tobias's comments regarding the source of the problem
Source Link
Aaron Harris
  • 2.7k
  • 18
  • 22

The variable mode-name is freea buffer-local variable that is also bound inside your lambdafunction sandric/kill-buffers-by-major-mode. Under dynamic scoping, it appearsthat means that the relevant value is destroyed whenof mode-name seen by your initially current buffer is killed, and subsequentcode will change when you change buffers use the value of the global variable of the same name. My guess is that(in this weird interaction is probably because that variable iscase, when you kill the current buffer-local). See the warning here about let bindings; it also applies to function arguments.

Whatever the exact cause, thisThis problem goes away if lambda is a proper closure; i.e.,for me if you define the function in ais defined using lexical scope, but according to lexically-scoped filethe manual. To do that, just put

-*- lexical-binding: t; -*-

anywhere on the first line of the file.

If you'd prefer not to do that, you could instead usethis is undefined behavior and using a special variable such as evalmode-name to define just this oneas a function lexically, like this:argument is discouraged.

(eval
 '(defun sandric/kill-buffers-by-major-mode (mode-name)
   "Kill open buffers by major mode name"
   (interactive)
   (mapc (lambda (buffer)
           (when (eq mode-name (buffer-local-value 'major-mode buffer))
             (kill-buffer buffer)))
         (buffer-list)))
 :lexical)

Or you could simplySo the correct solution is just to change the name of your function argument from mode-name to a different nameanother symbol that is unlikelynot defined as buffer-local.

(Thanks to already beTobias for explaining the details of the problematic interaction in usethe comments; my previous explanation was very sketchy, and I made some unfounded assumptions about what was causing the problem.)

The variable mode-name is free inside your lambda. Under dynamic scoping, it appears that the relevant value is destroyed when your initially current buffer is killed, and subsequent buffers use the value of the global variable of the same name. My guess is that this weird interaction is probably because that variable is buffer-local.

Whatever the exact cause, this problem goes away if lambda is a proper closure; i.e., if you define the function in a lexically-scoped file. To do that, just put

-*- lexical-binding: t; -*-

anywhere on the first line of the file.

If you'd prefer not to do that, you could instead use eval to define just this one function lexically, like this:

(eval
 '(defun sandric/kill-buffers-by-major-mode (mode-name)
   "Kill open buffers by major mode name"
   (interactive)
   (mapc (lambda (buffer)
           (when (eq mode-name (buffer-local-value 'major-mode buffer))
             (kill-buffer buffer)))
         (buffer-list)))
 :lexical)

Or you could simply change mode-name to a different name that is unlikely to already be in use.

The variable mode-name is a buffer-local variable that is also bound inside your function sandric/kill-buffers-by-major-mode. Under dynamic scoping, that means that the value of mode-name seen by your code will change when you change buffers (in this case, when you kill the current buffer). See the warning here about let bindings; it also applies to function arguments.

This problem goes away for me if the function is defined using lexical scope, but according to the manual, this is undefined behavior and using a special variable such as mode-name as a function argument is discouraged.

So the correct solution is just to change the name of your function argument from mode-name to another symbol that is not defined as buffer-local.

(Thanks to Tobias for explaining the details of the problematic interaction in the comments; my previous explanation was very sketchy, and I made some unfounded assumptions about what was causing the problem.)

Source Link
Aaron Harris
  • 2.7k
  • 18
  • 22

The variable mode-name is free inside your lambda. Under dynamic scoping, it appears that the relevant value is destroyed when your initially current buffer is killed, and subsequent buffers use the value of the global variable of the same name. My guess is that this weird interaction is probably because that variable is buffer-local.

Whatever the exact cause, this problem goes away if lambda is a proper closure; i.e., if you define the function in a lexically-scoped file. To do that, just put

-*- lexical-binding: t; -*-

anywhere on the first line of the file.

If you'd prefer not to do that, you could instead use eval to define just this one function lexically, like this:

(eval
 '(defun sandric/kill-buffers-by-major-mode (mode-name)
   "Kill open buffers by major mode name"
   (interactive)
   (mapc (lambda (buffer)
           (when (eq mode-name (buffer-local-value 'major-mode buffer))
             (kill-buffer buffer)))
         (buffer-list)))
 :lexical)

Or you could simply change mode-name to a different name that is unlikely to already be in use.