diff --git a/doc/lispref/variables.texi b/doc/lispref/variables.texi index 8779cf88917..5df1b890817 100644 --- a/doc/lispref/variables.texi +++ b/doc/lispref/variables.texi @@ -1748,6 +1748,14 @@ local hook functions that have a non-@code{nil} killed, but if the optional @var{kill-permanent} argument is non-@code{nil}, even those variables will be killed. +If @var{kill-permanent} is the symbol @code{permanent-local} the +function @code{kill-all-local-variables} kills local variables and +ignores any variable watchers. If it is the symbol @code{reset}, +variable watchers are ignored and the buffer is reset as if the buffer +was newly created. Use the foregoing with caution: For example, +@code{reset} sets buffer variables such as @code{default-directory} to +@code{nil} and may result in unexpected behavior. + This function also resets certain other information pertaining to the buffer: it sets the local keymap to @code{nil}, the syntax table to the value of @code{(standard-syntax-table)}, the case table to diff --git a/etc/NEWS b/etc/NEWS index 6678b632d41..a04f8af0832 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -137,6 +137,20 @@ To install the grammars, use 'M-x markdown-ts-mode-install-parsers'. * Lisp Changes in Emacs 32.1 ++++ +** 'kill-all-local-variables' can kill locals silently and reset the buffer. +This function's KILL-PERMANENT argument now accepts 'permanent-local' +which kills all locals ignoring any variable watchers, and also 'reset' +which does what 'permanent-local' does and also resets the buffer as if +newly created. Use these with caution to avoid unexpected behavior such +as 'default-directory' being reset to nil. + +--- +** 'with-work-buffer' kills all locals silently and resets its buffers. +This macro, when returning a buffer to its share buffer pool, now kills +all buffer locals silently, ignoring any variable watchers, and resets +the buffer as if newly created. + +++ ** The new function 'markers-in' returns the set of markers in a region. diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index d5a39b77c2e..08ef9961c1e 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -334,10 +334,10 @@ automatically killed, which means that in a such case (erase-buffer) (delete-all-overlays)) (let (change-major-mode-hook) - ;; `kill-all-local-variables' does not kill permanent locals - ;; like `buffer-read-only'. - (setq buffer-read-only nil) - (kill-all-local-variables t)) + ;; Ensure `kill-all-local-variables' kills *all* permanent locals + ;; rather than exempting any, and resets the buffer to pristine + ;; state. + (kill-all-local-variables 'reset)) ;; Make the buffer available again. (push buffer work-buffer--list))) ;; If the maximum number of reusable work buffers is exceeded, kill diff --git a/src/buffer.c b/src/buffer.c index 8963ec4e197..bafe86263ae 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -3031,15 +3031,33 @@ As a special exception, local variables whose names have a non-nil the optional KILL-PERMANENT argument is non-nil, clear out these local variables, too. +If KILL-PERMANENT is the symbol `permanent-local', kill local variables +and ignore variable watchers. If KILL-PERMANENT is the symbol `reset', +ignore variable watchers and reset the buffer as if newly created. Use +these with caution. For example, `reset' sets buffer variables such as +`default-directory' to nil and may result in unexpected behavior. + The first thing this function does is run the normal hook `change-major-mode-hook'. */) (Lisp_Object kill_permanent) { run_hook (Qchange_major_mode_hook); - /* Actually eliminate all local bindings of this buffer. */ + int permanent_too = 0; + if (!NILP (kill_permanent)) + { + permanent_too = 2; + if (EQ (kill_permanent, Qpermanent_local)) + permanent_too = 1; + else if (EQ (kill_permanent, Qreset)) + { + permanent_too = 1; + reset_buffer (current_buffer); + } + } - reset_buffer_local_variables (current_buffer, !NILP (kill_permanent) ? 2 : 0); + /* Actually eliminate all local bindings of this buffer. */ + reset_buffer_local_variables (current_buffer, permanent_too); /* Force mode-line redisplay. Useful here because all major mode commands call this function. */ diff --git a/test/lisp/emacs-lisp/subr-x-tests.el b/test/lisp/emacs-lisp/subr-x-tests.el index cad4dcbb7aa..cf9ed7d3fa2 100644 --- a/test/lisp/emacs-lisp/subr-x-tests.el +++ b/test/lisp/emacs-lisp/subr-x-tests.el @@ -815,5 +815,19 @@ (should (equal (string-truncate-left "band" 2) "...d")) (should (equal (string-truncate-left "longstring" 8) "...tring"))) +(ert-deftest subr-x-with-work-buffer-locals-killed () + (let (b) + (with-work-buffer + (setq b (current-buffer)) + (setq-local test-buffer-local 123) + (setq mark-active t) + (setq buffer-read-only t)) + (with-work-buffer + ;; Sanity check `with-work-buffer' yields the same buffer. + (should (eq b (current-buffer))) + (should-not (buffer-local-boundp 'test-buffer-local (current-buffer))) + (should-not mark-active) + (should-not buffer-read-only)))) + (provide 'subr-x-tests) ;;; subr-x-tests.el ends here