Subject: bug#17809: 24.4.50; Completions display

From: Juri Linkov <[email-masked]>
Date: Sat, 05 Jul 2014 02:40:31 +0300

>> Maybe it would be a good thing to add a new action parameter to
>> `display-buffer' that would be like the existing `quit-function'
>> in `with-current-buffer-window', and will call its body between
>> displaying the buffer and applying final actions alists like
>> (window-height . fit-window-to-buffer)
>
> `display-buffer' has no body and hardly ever will get one.

I agree that there is no way to add body to `display-buffer'.

So the cleanest solution would be to add a new macro
`with-displayed-buffer-window' that is like
`with-current-buffer-window' and `with-temp-buffer-window',
but will run its body in the displayed window.

I've tested it with different configurations, and it produces
nice results when used in the minibuffer, at point, in
horizontally split windows, in Dired *Marked Files*,
with pop-up-frames=t, etc.

=== modified file 'lisp/window.el'
--- lisp/window.el	2014-06-18 07:57:27 +0000
+++ lisp/window.el	2014-07-04 23:30:54 +0000
@@ -216,6 +216,35 @@ (defmacro with-current-buffer-window (bu
 	   (funcall ,quit-function ,window ,value)
 	 ,value))))
 
+(defmacro with-displayed-buffer-window (buffer-or-name action quit-function &rest body)
+  "Show a buffer BUFFER-OR-NAME and evaluate BODY in that buffer.
+This construct is like `with-current-buffer-window' but unlike that
+displays the buffer specified by BUFFER-OR-NAME before running BODY."
+  (declare (debug t))
+  (let ((buffer (make-symbol "buffer"))
+	(window (make-symbol "window"))
+	(value (make-symbol "value")))
+    `(let* ((,buffer (temp-buffer-window-setup ,buffer-or-name))
+	    (standard-output ,buffer)
+	    ,window ,value)
+       (with-current-buffer ,buffer
+	 (setq ,window (temp-buffer-window-show ,buffer ,action)))
+
+       (let ((inhibit-read-only t)
+	     (inhibit-modification-hooks t))
+	 (setq ,value (progn ,@body)))
+
+       (with-selected-window ,window
+	 (goto-char (point-min)))
+
+       (when (functionp (cdr (assq 'window-height (cdr ,action))))
+	 (ignore-errors
+	   (funcall (cdr (assq 'window-height (cdr ,action))) ,window)))
+
+       (if (functionp ,quit-function)
+	   (funcall ,quit-function ,window ,value)
+	 ,value))))
+
 ;; The following two functions are like `window-next-sibling' and
 ;; `window-prev-sibling' but the WINDOW argument is _not_ optional (so
 ;; they don't substitute the selected window for nil), and they return

=== modified file 'lisp/minibuffer.el'
--- lisp/minibuffer.el	2014-06-25 10:36:51 +0000
+++ lisp/minibuffer.el	2014-07-04 23:28:38 +0000
@@ -1794,8 +1794,29 @@ (defun minibuffer-completion-help (&opti
              ;; window, mark it as softly-dedicated, so bury-buffer in
              ;; minibuffer-hide-completions will know whether to
              ;; delete the window or not.
-             (display-buffer-mark-dedicated 'soft))
-        (with-output-to-temp-buffer "*Completions*"
+             (display-buffer-mark-dedicated 'soft)
+             ;; Disable `pop-up-windows' temporarily to allow
+             ;; `display-buffer--maybe-pop-up-frame-or-window'
+             ;; in the display actions below to pop up a frame
+             ;; if `pop-up-frames' is non-nil, but not to pop up a window.
+             (pop-up-windows nil))
+        (with-displayed-buffer-window
+	 "*Completions*"
+	 ;; This is a copy of `display-buffer-fallback-action'
+	 ;; where `display-buffer-use-some-window' is replaced
+	 ;; with `display-buffer-at-bottom'.
+	 `((display-buffer--maybe-same-window
+	    display-buffer-reuse-window
+	    display-buffer--maybe-pop-up-frame-or-window
+	    ;; Use `display-buffer-below-selected' for inline completions,
+	    ;; but not in the minibuffer (e.g. in `eval-expression')
+	    ;; for which `display-buffer-at-bottom' is used.
+	    ,(if (and completion-in-region-mode-predicate
+		      (not (minibuffer-selected-window)))
+		 'display-buffer-below-selected
+	       'display-buffer-at-bottom))
+	   (window-height . fit-window-to-buffer))
+	 nil
           ;; Remove the base-size tail because `sort' requires a properly
           ;; nil-terminated list.
           (when last (setcdr last nil))

=== modified file 'lisp/dired.el'
--- lisp/dired.el	2014-06-21 19:45:59 +0000
+++ lisp/dired.el	2014-07-04 23:30:52 +0000
@@ -3103,8 +3103,7 @@ (defun dired-mark-pop-up (buffer-or-name
 	  ;; 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-current-buffer buffer
-	(with-current-buffer-window
+      (with-displayed-buffer-window
 	 buffer
 	 (cons 'display-buffer-below-selected
 	       '((window-height . fit-window-to-buffer)))
@@ -3117,6 +3116,7 @@ (defun dired-mark-pop-up (buffer-or-name
 	 ;; 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)