Subject: Re: bug#39822: 27.0.90; Cannot set *Completions* buffer height using display-buffer-alist

From: Juri Linkov <[email-masked]>
Date: Sun, 29 Mar 2020 01:36:27 +0200

>> But I already agreed about adding a new arg to
>> 'after-display-function', and will add it anyway.
>
> OK.  To avoid confusions 'after-display-function' should be renamed to
> 'after-display-buffer-function' at least.

Are you sure about such long name?  This is not a hook, it's just
an alist entry along with 'window-height' and 'preserve-size'.

A good short name would be 'body-function' where 'body' has
two-fold meaning:

1. it hints to body of the former macro that it replaces;

2. body could also mean window body that this function fills.

Here is a completely tested patch that works in all cases:

diff --git a/lisp/window.el b/lisp/window.el
index b54f1633f5..00e793db95 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -7070,6 +7070,12 @@ window--display-buffer
         (set-window-dedicated-p window display-buffer-mark-dedicated))))
     (when (memq type '(window frame tab))
       (set-window-prev-buffers window nil))
+
+    (when (functionp (cdr (assq 'body-function alist)))
+      (let ((inhibit-read-only t)
+            (inhibit-modification-hooks t))
+        (funcall (cdr (assq 'body-function alist)) window)))
+
     (let ((quit-restore (window-parameter window 'quit-restore))
 	  (height (cdr (assq 'window-height alist)))
 	  (width (cdr (assq 'window-width alist)))
diff --git a/lisp/dired.el b/lisp/dired.el
index 41bbf9f56a..51ec9a798e 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -3520,26 +3521,27 @@ dired-mark-pop-up
 	  ;; Mark *Marked Files* window as softly-dedicated, to prevent
 	  ;; other buffers e.g. *Completions* from reusing it (bug#17554).
 	  (display-buffer-mark-dedicated 'soft))
-      (with-displayed-buffer-window
+      (with-current-buffer-window
        buffer
-       (cons 'display-buffer-below-selected
-	     '((window-height . fit-window-to-buffer)
-	       (preserve-size . (nil . t))))
+       `(display-buffer-below-selected
+         (window-height . fit-window-to-buffer)
+         (preserve-size . (nil . t))
+         (body-function
+          . ,#'(lambda (_window)
+                 ;; Handle (t FILE) just like (FILE), here.  That value is
+                 ;; used (only in some cases), to mean just one file that was
+                 ;; marked, rather than the current line file.
+                 (dired-format-columns-of-files
+                  (if (eq (car files) t) (cdr files) files))
+                 (remove-text-properties (point-min) (point-max)
+                                         '(mouse-face nil help-echo nil))
+                 (setq tab-line-exclude nil))))
        #'(lambda (window _value)
 	   (with-selected-window window
 	     (unwind-protect
 		 (apply function args)
 	       (when (window-live-p window)
-		 (quit-restore-window window 'kill)))))
-       ;; Handle (t FILE) just like (FILE), here.  That value is
-       ;; used (only in some cases), to mean just one file that was
-       ;; marked, rather than the current line file.
-       (with-current-buffer buffer
-	 (dired-format-columns-of-files
-	  (if (eq (car files) t) (cdr files) files))
-	 (remove-text-properties (point-min) (point-max)
-				 '(mouse-face nil help-echo nil))
-	 (setq tab-line-exclude nil))))))
+		 (quit-restore-window window 'kill)))))))))
 
 (defun dired-format-columns-of-files (files)
   (let ((beg (point)))
diff --git a/lisp/files.el b/lisp/files.el
index 8ce0187f5b..4b5c7d1e55 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -7253,10 +7253,15 @@ save-buffers-kill-emacs
                   (setq active t))
              (setq processes (cdr processes)))
            (or (not active)
-               (with-displayed-buffer-window
+               (with-current-buffer-window
                 (get-buffer-create "*Process List*")
-                '(display-buffer--maybe-at-bottom
-                  (dedicated . t))
+                `(display-buffer--maybe-at-bottom
+                  (dedicated . t)
+                  (window-height . fit-window-to-buffer)
+                  (preserve-size . (nil . t))
+                  (body-function
+                   . ,#'(lambda (_window)
+                          (list-processes t))))
                 #'(lambda (window _value)
                     (with-selected-window window
                       (unwind-protect
@@ -7264,8 +7269,7 @@ save-buffers-kill-emacs
                             (setq confirm nil)
                             (yes-or-no-p "Active processes exist; kill them and exit anyway? "))
                         (when (window-live-p window)
-                          (quit-restore-window window 'kill)))))
-                (list-processes t)))))
+                          (quit-restore-window window 'kill)))))))))
      ;; Query the user for other things, perhaps.
      (run-hook-with-args-until-failure 'kill-emacs-query-functions)
      (or (null confirm)
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 7f5b597542..d94582a908 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -1973,7 +1973,7 @@ minibuffer-completion-help
              ;; minibuffer-hide-completions will know whether to
              ;; delete the window or not.
              (display-buffer-mark-dedicated 'soft))
-        (with-displayed-buffer-window
+        (with-current-buffer-window
           "*Completions*"
           ;; This is a copy of `display-buffer-fallback-action'
           ;; where `display-buffer-use-some-window' is replaced
@@ -1991,62 +1991,64 @@ minibuffer-completion-help
 		 '(window-height . resize-temp-buffer-window)
 	       '(window-height . fit-window-to-buffer))
 	    ,(when temp-buffer-resize-mode
-	       '(preserve-size . (nil . t))))
-          nil
-          ;; Remove the base-size tail because `sort' requires a properly
-          ;; nil-terminated list.
-          (when last (setcdr last nil))
-          (setq completions
-                ;; FIXME: This function is for the output of all-completions,
-                ;; not completion-all-completions.  Often it's the same, but
-                ;; not always.
-                (let ((sort-fun (completion-metadata-get
-                                 all-md 'display-sort-function)))
-                  (if sort-fun
-                      (funcall sort-fun completions)
-                    (sort completions 'string-lessp))))
-          (when afun
-            (setq completions
-                  (mapcar (lambda (s)
-                            (let ((ann (funcall afun s)))
-                              (if ann (list s ann) s)))
-                          completions)))
+	       '(preserve-size . (nil . t)))
+            (body-function
+             . ,#'(lambda (_window)
+                    ;; Remove the base-size tail because `sort' requires a properly
+                    ;; nil-terminated list.
+                    (when last (setcdr last nil))
+                    (setq completions
+                          ;; FIXME: This function is for the output of all-completions,
+                          ;; not completion-all-completions.  Often it's the same, but
+                          ;; not always.
+                          (let ((sort-fun (completion-metadata-get
+                                           all-md 'display-sort-function)))
+                            (if sort-fun
+                                (funcall sort-fun completions)
+                              (sort completions 'string-lessp))))
+                    (when afun
+                      (setq completions
+                            (mapcar (lambda (s)
+                                      (let ((ann (funcall afun s)))
+                                        (if ann (list s ann) s)))
+                                    completions)))
 
-          (with-current-buffer standard-output
-            (set (make-local-variable 'completion-base-position)
-                 (list (+ start base-size)
-                       ;; FIXME: We should pay attention to completion
-                       ;; boundaries here, but currently
-                       ;; completion-all-completions does not give us the
-                       ;; necessary information.
-                       end))
-            (set (make-local-variable 'completion-list-insert-choice-function)
-                 (let ((ctable minibuffer-completion-table)
-                       (cpred minibuffer-completion-predicate)
-                       (cprops completion-extra-properties))
-                   (lambda (start end choice)
-                     (unless (or (zerop (length prefix))
-                                 (equal prefix
-                                        (buffer-substring-no-properties
-                                         (max (point-min)
-                                              (- start (length prefix)))
-                                         start)))
-                       (message "*Completions* out of date"))
-                     ;; FIXME: Use `md' to do quoting&terminator here.
-                     (completion--replace start end choice)
-                     (let* ((minibuffer-completion-table ctable)
-                            (minibuffer-completion-predicate cpred)
-                            (completion-extra-properties cprops)
-                            (result (concat prefix choice))
-                            (bounds (completion-boundaries
-                                     result ctable cpred "")))
-                       ;; If the completion introduces a new field, then
-                       ;; completion is not finished.
-                       (completion--done result
-                                         (if (eq (car bounds) (length result))
-                                             'exact 'finished)))))))
+                    (with-current-buffer standard-output
+                      (set (make-local-variable 'completion-base-position)
+                           (list (+ start base-size)
+                                 ;; FIXME: We should pay attention to completion
+                                 ;; boundaries here, but currently
+                                 ;; completion-all-completions does not give us the
+                                 ;; necessary information.
+                                 end))
+                      (set (make-local-variable 'completion-list-insert-choice-function)
+                           (let ((ctable minibuffer-completion-table)
+                                 (cpred minibuffer-completion-predicate)
+                                 (cprops completion-extra-properties))
+                             (lambda (start end choice)
+                               (unless (or (zerop (length prefix))
+                                           (equal prefix
+                                                  (buffer-substring-no-properties
+                                                   (max (point-min)
+                                                        (- start (length prefix)))
+                                                   start)))
+                                 (message "*Completions* out of date"))
+                               ;; FIXME: Use `md' to do quoting&terminator here.
+                               (completion--replace start end choice)
+                               (let* ((minibuffer-completion-table ctable)
+                                      (minibuffer-completion-predicate cpred)
+                                      (completion-extra-properties cprops)
+                                      (result (concat prefix choice))
+                                      (bounds (completion-boundaries
+                                               result ctable cpred "")))
+                                 ;; If the completion introduces a new field, then
+                                 ;; completion is not finished.
+                                 (completion--done result
+                                                   (if (eq (car bounds) (length result))
+                                                       'exact 'finished)))))))
 
-          (display-completion-list completions))))
+                    (display-completion-list completions))))
+          nil)))
     nil))
 
 (defun minibuffer-hide-completions ()