master bfc1998fb94: New macro without-local-variable-queries (bug#81233)

Sean Whitton <[email protected]> Tue, 7 Jul 2026 09:30:38 -0400 (EDT)
Newsgroups gmane.emacs.diffs
Message-ID <[email protected]>
branch: master
commit bfc1998fb9401f85b97f2f67149c912d89606188
Author: Sean Whitton <[email protected]>
Commit: Sean Whitton <[email protected]>

    New macro without-local-variable-queries (bug#81233)
    
    * lisp/files.el (without-local-variable-queries): New macro.
    * lisp/vc/diff-mode.el (diff-syntax-fontify-props):
    * lisp/vc/vc-dir.el (vc-dir--count-outgoing):
    * lisp/vc/vc.el (vc-find-revision-no-save): Use it.
    * lisp/files.el (hack-local-variables-confirm): Warn when it
    looks like the caller should have used it.
---
 lisp/files.el        | 15 ++++++++++++
 lisp/vc/diff-mode.el | 38 +++++++++++++++---------------
 lisp/vc/vc-dir.el    | 65 ++++++++++++++++++++++++++--------------------------
 lisp/vc/vc.el        | 20 ++++++++--------
 4 files changed, 76 insertions(+), 62 deletions(-)

diff --git a/lisp/files.el b/lisp/files.el
index fa120f5c8ae..27a69b3fc81 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -3991,6 +3991,18 @@ variable `enable-remote-dir-locals' is non-nil."
   :risky t
   :group 'find-file)
 
+(defmacro without-local-variable-queries (&rest body)
+  "Execute BODY without querying user about local variable values.
+In some uses, this is a workaround for the problem that a command
+disables displaying new windows for its own reasons but this also breaks
+`hack-local-variables-confirm'.  See Emacs bug#80528 and bug#81233."
+  (declare (indent 0) (debug t))
+  `(let ((enable-local-variables
+          (if (memq enable-local-variables '(:safe :all nil))
+              enable-local-variables
+            :safe)))
+     ,@body))
+
 (defun hack-local-variables-confirm (all-vars unsafe-vars risky-vars dir-name)
   "Get confirmation before setting up local variable values.
 ALL-VARS is the list of all variables to be set up.
@@ -4055,6 +4067,9 @@ i  -- to ignore the local variables list, and permanently mark these
       ;; Display the buffer and read a choice.
       (save-window-excursion
 	(pop-to-buffer buf '(display-buffer--maybe-at-bottom))
+        (unless (get-buffer-window buf)
+          (error "Failed to display local variables buffer; this is a bug
+Possibly caller should use `without-local-variable-queries', which see."))
 	(let* ((exit-chars '(?y ?n ?\s))
 	       (prompt (format "Please type %s%s: "
 			       (if offer-save
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index c1227573351..0ee055424c8 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -3504,25 +3504,25 @@ hunk text is not found in the source file."
     ;; When initialization is requested, we should be in a brand new
     ;; temp buffer.
     (cl-assert (null buffer-file-name))
-    ;; Use `:safe' to find `mode:'.  In case of hunk-only, use nil because
-    ;; Local Variables list might be incomplete when context is truncated.
-    (let ((enable-local-variables
-           (unless hunk-only
-             (if (memq enable-local-variables '(:safe :all nil))
-                 enable-local-variables
-               ;; Ignore other values that query.
-               :safe)))
-          (buffer-file-name file))
-      ;; Don't run hooks that might assume buffer-file-name
-      ;; really associates buffer with a file (bug#39190).
-      (delay-mode-hooks (set-auto-mode))
-      ;; FIXME: Is this really worth the trouble?
-      (when (and (fboundp 'generic-mode-find-file-hook)
-                 (memq #'generic-mode-find-file-hook
-                       ;; There's no point checking the buffer-local value,
-                       ;; we're in a fresh new buffer.
-                       (default-value 'find-file-hook)))
-        (generic-mode-find-file-hook))))
+    (cl-flet
+        ((set-mode ()
+           ;; Don't run hooks that might assume buffer-file-name
+           ;; really associates buffer with a file (bug#39190).
+           (delay-mode-hooks (set-auto-mode))
+           ;; FIXME: Is this really worth the trouble?
+           (when (and (fboundp 'generic-mode-find-file-hook)
+                      (memq #'generic-mode-find-file-hook
+                            ;; There's no point checking the
+                            ;; buffer-local value because we're in a
+                            ;; fresh new buffer.
+                            (default-value 'find-file-hook)))
+             (generic-mode-find-file-hook))))
+      ;; Use `:safe' to find `mode:'.  In case of hunk-only, use nil because
+      ;; Local Variables list might be incomplete when context is truncated.
+      (let ((buffer-file-name file))
+        (if hunk-only
+            (let (enable-local-variables) (set-mode))
+          (without-local-variable-queries (set-mode))))))
 
   (let ((font-lock-defaults (or font-lock-defaults '(nil t)))
         props beg end)
diff --git a/lisp/vc/vc-dir.el b/lisp/vc/vc-dir.el
index ff019bbbb8d..383e55e8444 100644
--- a/lisp/vc/vc-dir.el
+++ b/lisp/vc/vc-dir.el
@@ -1534,39 +1534,40 @@ uses OVERLAY."
              (unknown (propertize "<<unknown>>" 'face 'vc-dir-header-value))
              (buf (generate-new-buffer " *temp*" t))
              proc)
-        (with-current-buffer buf
-          (condition-case _
-              (progn
-                (vc-incoming-outgoing-internal backend nil
-                                               (current-buffer)
-                                               '(log-outgoing short))
-                (setq proc (get-buffer-process (current-buffer)))
-                (overlay-put overlay 'proc proc)
-                (vc-run-delayed
-                  (unwind-protect
-                      (overlay-put
-                       overlay 'after-string
-                       (if (or (not (eq (process-status proc) 'exit))
-                               (plusp (process-exit-status proc)))
-                           unknown
-                         (goto-char (point-min))
-                         (let ((count (how-many log-view-message-re)))
-                           (if (zerop count)
-                               (propertize "No unpushed revisions"
-                                           'face 'vc-dir-header-value)
-                             (propertize
-                              (format (ngettext "%d unpushed revision"
-                                                "%d unpushed revisions"
-                                                count)
-                                      count)
-                              'face 'vc-dir-header-urgent-value
-                              'mouse-face 'highlight
-                              'keymap vc-dir-outgoing-revisions-map
-                              'help-echo "\\<vc-dir-outgoing-revisions-map>\
+        (without-local-variable-queries
+          (with-current-buffer buf
+            (condition-case _
+                (progn
+                  (vc-incoming-outgoing-internal backend nil
+                                                 (current-buffer)
+                                                 '(log-outgoing short))
+                  (setq proc (get-buffer-process (current-buffer)))
+                  (overlay-put overlay 'proc proc)
+                  (vc-run-delayed
+                    (unwind-protect
+                        (overlay-put
+                         overlay 'after-string
+                         (if (or (not (eq (process-status proc) 'exit))
+                                 (plusp (process-exit-status proc)))
+                             unknown
+                           (goto-char (point-min))
+                           (let ((count (how-many log-view-message-re)))
+                             (if (zerop count)
+                                 (propertize "No unpushed revisions"
+                                             'face 'vc-dir-header-value)
+                               (propertize
+                                (format (ngettext "%d unpushed revision"
+                                                  "%d unpushed revisions"
+                                                  count)
+                                        count)
+                                'face 'vc-dir-header-urgent-value
+                                'mouse-face 'highlight
+                                'keymap vc-dir-outgoing-revisions-map
+                                'help-echo "\\<vc-dir-outgoing-revisions-map>\
 \\[vc-root-log-outgoing]: List outgoing revisions")))))
-                    (kill-buffer))))
-            (error (overlay-put overlay 'after-string unknown)
-                   (kill-buffer buf)))))))))
+                      (kill-buffer))))
+              (error (overlay-put overlay 'after-string unknown)
+                     (kill-buffer buf))))))))))
 
 (defvar-local vc-dir-async-header-values
   '(("Outgoing" . vc-dir--count-outgoing))
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index 33e3e5dced2..766551ca1ef 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -3871,17 +3871,15 @@ Unlike `vc-find-revision-save', doesn't save the buffer to the file."
                 (after-insert-file-set-coding (- (point-max) (point-min)))
                 (goto-char (point-min))
                 (if buffer
-                    ;; For non-interactive, skip any questions
-                    (let ((enable-local-variables
-                           (if (memq enable-local-variables '(:safe :all nil))
-                               enable-local-variables
-                             ;; Ignore other values that query,
-                             ;; use `:safe' to find `mode:'.
-                             :safe))
-                          (buffer-file-name file))
-                      ;; Don't run hooks that might assume buffer-file-name
-                      ;; really associates buffer with a file (bug#39190).
-                      (ignore-errors (delay-mode-hooks (set-auto-mode))))
+                    ;; For non-interactive, skip any questions.
+                    ;; Use `:safe' to find `mode:'.
+                    (without-local-variable-queries
+                      (let ((buffer-file-name file))
+                        ;; Don't run hooks that might assume
+                        ;; buffer-file-name really associates buffer
+                        ;; with a file (bug#39190).
+                        (ignore-errors
+                          (delay-mode-hooks (set-auto-mode)))))
                   ;; Use non-nil 'find-file' arg of 'normal-mode'
                   ;; to not ignore 'enable-local-variables' when nil.
                   (normal-mode (not enable-local-variables)))