Subject: Re: bug#32790: 27.0.50; point jumps unexpectedly after delete-window

From: Juri Linkov <[email-masked]>
Date: Thu, 22 Nov 2018 01:35:00 +0200

>> 0. emacs -Q
>>
>> 1. C-h e
>>
>> 2. C-x o
>>
>> 3. C-x 2
>>
>> 4. C-x 0
>>
>> It's expected that point will remain where it was visually on the screen,
>> just will relocate to the window that takes place of the deleted window,
>> but point jumps to an unexpected place in the opposite part of the frame.
>
> What should Emacs do instead?

One possible solution is to implement directional window deletion.
The following patch allows using the prefix argument that will delete
the selected window and select the window at the given direction.

This makes possible this workflow:

0. emacs -Q -mm

1. eval: (windmove-delete-default-keybindings)

2. C-h e

3. C-x o

4. C-x 2

5. C-h i

6. C-u C-x S-down

and after deleting of the selected window, the cursor stays in the same place,
and doesn't jump to the opposite part of the frame.

diff --git a/lisp/windmove.el b/lisp/windmove.el
index 898f87e2db..9a7a2b80f2 100644
--- a/lisp/windmove.el
+++ b/lisp/windmove.el
@@ -678,6 +678,74 @@ windmove-display-default-keybindings
   (global-set-key (vector (append modifiers '(down)))  'windmove-display-down)
   (global-set-key (vector (append modifiers '(?0)))    'windmove-display-same-window))
 
+;;; Directional window deletion
+
+(defun windmove-delete-in-direction (dir &optional arg)
+  "Delete the window at direction DIR.
+If prefix ARG is `C-u', delete the selected window and
+select the window at direction DIR."
+  (let ((other-window (window-in-direction dir nil nil arg
+                                           windmove-wrap-around t)))
+    (cond ((null other-window)
+           (user-error "No window %s from selected window" dir))
+          ((and (window-minibuffer-p other-window)
+                (not (minibuffer-window-active-p other-window)))
+           (user-error "Minibuffer is inactive"))
+          (t
+           (if (not (consp arg))
+               (delete-window other-window)
+             (delete-window (selected-window))
+             (select-window other-window))))))
+
+;;;###autoload
+(defun windmove-delete-left (&optional arg)
+  "Delete the window to the left of the current one.
+If prefix ARG is `C-u', delete the selected window and
+select the window that was to the left of the current one."
+  (interactive "P")
+  (windmove-delete-in-direction 'left arg))
+
+;;;###autoload
+(defun windmove-delete-up (&optional arg)
+  "Delete the window above the current one.
+If prefix ARG is `C-u', delete the selected window and
+select the window that was above the current one."
+  (interactive "P")
+  (windmove-delete-in-direction 'up arg))
+
+;;;###autoload
+(defun windmove-delete-right (&optional arg)
+  "Delete the window to the right of the current one.
+If prefix ARG is `C-u', delete the selected window and
+select the window that was to the right of the current one."
+  (interactive "P")
+  (windmove-delete-in-direction 'right arg))
+
+;;;###autoload
+(defun windmove-delete-down (&optional arg)
+  "Delete the window below the current one.
+If prefix ARG is `C-u', delete the selected window and
+select the window that was below the current one."
+  (interactive "P")
+  (windmove-delete-in-direction 'down arg))
+
+;;;###autoload
+(defun windmove-delete-default-keybindings (&optional prefix modifiers)
+  "Set up keybindings for directional window deletion.
+Keys are bound to commands that delete windows in the specified
+direction.  Keybindings are of the form PREFIX MODIFIERS-{left,right,up,down},
+where PREFIX is a prefix key and MODIFIERS is either a list of modifiers or
+a single modifier.  Default value of PREFIX is `C-x' and MODIFIERS is `shift'."
+  (interactive)
+  (unless prefix (setq prefix '(?\C-x)))
+  (unless (listp prefix) (setq prefix (list prefix)))
+  (unless modifiers (setq modifiers '(shift)))
+  (unless (listp modifiers) (setq modifiers (list modifiers)))
+  (global-set-key (vector prefix (append modifiers '(left)))  'windmove-delete-left)
+  (global-set-key (vector prefix (append modifiers '(right))) 'windmove-delete-right)
+  (global-set-key (vector prefix (append modifiers '(up)))    'windmove-delete-up)
+  (global-set-key (vector prefix (append modifiers '(down)))  'windmove-delete-down))
+
 (provide 'windmove)
 
 ;;; windmove.el ends here