Subject: bug#28814: [BUMP, PATCH] (26.0.90; When *xref* window is needed, original window-switching intent is lost )

From: [email-masked] (João Távora)
Date: Mon, 23 Oct 2017 09:23:05 +0100

Dmitry Gutov <[email-masked]> writes:

> You are working on something that I agree is a real problem, but doing
> this would effectively revert commit
> 5698947ff9335cf150c73cca257a5e7e5951b045, which was based on a
> previous discussion (see the link in the commit message), and in
> particular, would bring back "disappearing *xref* buffer problem", as
> Eli put it.
> [...]
>
> I have a rough idea on how to fix the situation, but nothing even
> close to an implementation.
>
Thanks for the pointer. After I read that discussion I will probably ask
you about that.

>>     emacs -Q
>>     C-x 3 [split-window-right]
>>     C-x 2 [split-window-below]
>>     M-. xref-backend-definitions RET [xref-find-definitions]
>>     C-n [next-line]
>>     RET [xref-goto-xref]
>>
>> Expected the definition to be found in the original window where I
>> pressed M-. but instead it was found in another. Another case:
>>
>>     emacs -Q
>>     C-x 4 . xref-backend-definitions RET [xref-find-definitions-other-window]
>>     C-n
>>     RET
>>
>> Expected the definition to be found in some other window, different from
>> the one I pressed M-. on. Instead went to the same one.
>
> With your patches applied, this example pops a new frame for me if I
> press 'n' instead of 'C-n'. This is not hugely important in the light
> of the larger problems above, but demonstrated difficulties with
> window management when we try to pretend that the xref buffer "was
> never there".

You are absolutely right (and you don't have to tell me how hairy
window-management code is). This particular problem, which I had also
noticed, slipped through. I have a fix attached as a patch. 

The cause of this problem is that split-window-sensibly refuses to split
a window whose dimensions are below those of split-height-threshold and
split-width-threshold. The reason you don't see frames popping up every
time you do C-x 4 b on a small frame is that this function contains a
safety net for these cases: if the window to be split is the only one
available in the frame, it disregards the dimension threshholds and
splits anyway. The attached window.el patch is a correct way to
generalize this to account for dedicated windows.

If this is not accepted this local fix in xref.el will also work fine.

   @@ -504,7 +504,8 @@ xref--show-location
                  (t
                   (save-selected-window
                     (xref--with-dedicated-window
   -                  (xref--show-pos-in-buf marker buf))))))
   +                  (let ((split-height-threshold 0))
   +                    (xref--show-pos-in-buf marker buf)))))))
        (user-error (message (error-message-string err)))))
    
    (defun xref-show-location-at-point ()

>> Also, in both
>> situations, expected the window configuration to be the same as if I had
>> searched for, say xref-backend-functions.
>>
>> This is fixed by the patches that I reattach after minor tweaks. The
>> general idea is to have the *xref*, whose sudden appearance is hard to
>> predict, obtrude as little as possible on the user's window
>> configuration.p
>
> If we don't bring back the "disappearing *xref* buffer problem",
> *xref* has to stay obtrusive. Do you think the rest of your patch will
> make sense with that change?

I see and I will try to answer. I proposed two patches previously:

* a first one to fix the non-determinism of window popping/selecting
behaviour;
* a second one to make the *xref* buffer less obstrusive.
* (now there is the third one that fixes the frame-popping glitch)

IIUC it is the second one that clashes with "the dissapearing *xref*
problem" that I have yet to read up on. If we don't come up with a
solution for that, I would be OK with a solution that leaves it unsolved
but adds some customization point (hook) for the user to put this
behaviour in.

João

>From 47480926f328db5d840ba518125e0866d29f8665 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= <[email-masked]>
Date: Mon, 23 Oct 2017 09:05:32 +0100
Subject: [PATCH 3/3] Allow split-window-sensibly to split threshold in further
 edge case

As a fallback, and to avoid creating a frame, split-window-sensibly
would previously disregard split-height-threshold if the window to be
split is the frame's root window.

This change slightly expands on that: it disregards the threshold if
the window to be split is the frame's only usable window (it is either
the only one, as before, or all the other windows are dedicated to
some buffer and thus cannot be touched).

* lisp/window.el (split-height-threshold): Adjust doc to match
split-window-sensibly.
(split-window-sensibly): Also disregard threshold if all other
windows are dedicated.
---
 lisp/window.el | 33 ++++++++++++++++++++++-----------
 1 file changed, 22 insertions(+), 11 deletions(-)

diff --git a/lisp/window.el b/lisp/window.el
index 5ba9a305f9..21271944c0 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -6449,8 +6449,9 @@ split-height-threshold
 vertically only if it has at least this many lines.  If this is
 nil, `split-window-sensibly' is not allowed to split a window
 vertically.  If, however, a window is the only window on its
-frame, `split-window-sensibly' may split it vertically
-disregarding the value of this variable."
+frame, or all the other ones are dedicated,
+`split-window-sensibly' may split it vertically disregarding the
+value of this variable."
   :type '(choice (const nil) (integer :tag "lines"))
   :version "23.1"
   :group 'windows)
@@ -6557,15 +6558,25 @@ split-window-sensibly
 	     ;; Split window horizontally.
 	     (with-selected-window window
 	       (split-window-right)))
-	(and (eq window (frame-root-window (window-frame window)))
-	     (not (window-minibuffer-p window))
-	     ;; If WINDOW is the only window on its frame and is not the
-	     ;; minibuffer window, try to split it vertically disregarding
-	     ;; the value of `split-height-threshold'.
-	     (let ((split-height-threshold 0))
-	       (when (window-splittable-p window)
-		 (with-selected-window window
-		   (split-window-below))))))))
+	(and
+         (let ((frame (window-frame window)))
+           (or
+            (eq window (frame-root-window frame))
+            (let ((windows (delete window (window-list frame)))
+                  w)
+              (while (and (setq w (pop windows))
+                          (window-dedicated-p w)))
+              (not windows))))
+	 (not (window-minibuffer-p window))
+	 ;; If WINDOW is the only usable window on its frame (it is
+	 ;; the only one or,not being the only one, all the other ones
+	 ;; are dedicated) and is not the minibuffer window, try to
+	 ;; split it vertically disregarding the value of
+	 ;; `split-height-threshold'.
+	 (let ((split-height-threshold 0))
+	   (when (window-splittable-p window)
+	     (with-selected-window window
+	       (split-window-below))))))))
 
 (defun window--try-to-split-window (window &optional alist)
   "Try to split WINDOW.
-- 
2.14.2