diff options
Diffstat (limited to 'lisp/windmove.el')
-rw-r--r-- | lisp/windmove.el | 238 |
1 files changed, 214 insertions, 24 deletions
diff --git a/lisp/windmove.el b/lisp/windmove.el index db77d810e05..6d61806a831 100644 --- a/lisp/windmove.el +++ b/lisp/windmove.el @@ -1,4 +1,4 @@ -;;; windmove.el --- directional window-selection routines +;;; windmove.el --- directional window-selection routines -*- lexical-binding:t -*- ;; ;; Copyright (C) 1998-2018 Free Software Foundation, Inc. ;; @@ -149,6 +149,15 @@ is inactive." :type 'boolean :group 'windmove) +(defcustom windmove-create-window nil + "Whether movement off the edge of the frame creates a new window. +If this variable is set to t, moving left from the leftmost window in +a frame will create a new window on the left, and similarly for the other +directions." + :type 'boolean + :group 'windmove + :version "27.1") + ;; If your Emacs sometimes places an empty column between two adjacent ;; windows, you may wish to set this delta to 2. (defcustom windmove-window-distance-delta 1 @@ -464,20 +473,22 @@ movement is relative to." (defun windmove-find-other-window (dir &optional arg window) "Return the window object in direction DIR. DIR, ARG, and WINDOW are handled as by `windmove-other-window-loc'." - (window-in-direction - (cond - ((eq dir 'up) 'above) - ((eq dir 'down) 'below) - (t dir)) - window nil arg windmove-wrap-around t)) + (window-in-direction dir window nil arg windmove-wrap-around t)) ;; Selects the window that's hopefully at the location returned by ;; `windmove-other-window-loc', or screams if there's no window there. (defun windmove-do-window-select (dir &optional arg window) "Move to the window at direction DIR. DIR, ARG, and WINDOW are handled as by `windmove-other-window-loc'. -If no window is at direction DIR, an error is signaled." +If no window is at direction DIR, an error is signaled. +If `windmove-create-window' is non-nil, try to create a new window +in direction DIR instead." (let ((other-window (windmove-find-other-window dir arg window))) + (when (and windmove-create-window + (or (null other-window) + (and (window-minibuffer-p other-window) + (not (minibuffer-window-active-p other-window))))) + (setq other-window (split-window window nil dir))) (cond ((null other-window) (user-error "No window %s from selected window" dir)) ((and (window-minibuffer-p other-window) @@ -498,9 +509,10 @@ With no prefix argument, or with prefix argument equal to zero, \"left\" is relative to the position of point in the window; otherwise it is relative to the top edge (for positive ARG) or the bottom edge \(for negative ARG) of the current window. -If no window is at the desired location, an error is signaled." +If no window is at the desired location, an error is signaled +unless `windmove-create-window' is non-nil and a new window is created." (interactive "P") - (windmove-do-window-select 'left arg)) + (windmove-do-window-select 'left (and arg (prefix-numeric-value arg)))) ;;;###autoload (defun windmove-up (&optional arg) @@ -509,9 +521,10 @@ With no prefix argument, or with prefix argument equal to zero, \"up\" is relative to the position of point in the window; otherwise it is relative to the left edge (for positive ARG) or the right edge (for negative ARG) of the current window. -If no window is at the desired location, an error is signaled." +If no window is at the desired location, an error is signaled +unless `windmove-create-window' is non-nil and a new window is created." (interactive "P") - (windmove-do-window-select 'up arg)) + (windmove-do-window-select 'up (and arg (prefix-numeric-value arg)))) ;;;###autoload (defun windmove-right (&optional arg) @@ -520,9 +533,10 @@ With no prefix argument, or with prefix argument equal to zero, \"right\" is relative to the position of point in the window; otherwise it is relative to the top edge (for positive ARG) or the bottom edge (for negative ARG) of the current window. -If no window is at the desired location, an error is signaled." +If no window is at the desired location, an error is signaled +unless `windmove-create-window' is non-nil and a new window is created." (interactive "P") - (windmove-do-window-select 'right arg)) + (windmove-do-window-select 'right (and arg (prefix-numeric-value arg)))) ;;;###autoload (defun windmove-down (&optional arg) @@ -531,9 +545,10 @@ With no prefix argument, or with prefix argument equal to zero, \"down\" is relative to the position of point in the window; otherwise it is relative to the left edge (for positive ARG) or the right edge \(for negative ARG) of the current window. -If no window is at the desired location, an error is signaled." +If no window is at the desired location, an error is signaled +unless `windmove-create-window' is non-nil and a new window is created." (interactive "P") - (windmove-do-window-select 'down arg)) + (windmove-do-window-select 'down (and arg (prefix-numeric-value arg)))) ;;; set up keybindings @@ -543,17 +558,192 @@ If no window is at the desired location, an error is signaled." ;; probably want to use different bindings in that case. ;;;###autoload -(defun windmove-default-keybindings (&optional modifier) +(defun windmove-default-keybindings (&optional modifiers) "Set up keybindings for `windmove'. -Keybindings are of the form MODIFIER-{left,right,up,down}. -Default MODIFIER is `shift'." +Keybindings are of the form MODIFIERS-{left,right,up,down}, +where MODIFIERS is either a list of modifiers or a single modifier. +Default value of MODIFIERS is `shift'." (interactive) - (unless modifier (setq modifier 'shift)) - (global-set-key (vector (list modifier 'left)) 'windmove-left) - (global-set-key (vector (list modifier 'right)) 'windmove-right) - (global-set-key (vector (list modifier 'up)) 'windmove-up) - (global-set-key (vector (list modifier 'down)) 'windmove-down)) + (unless modifiers (setq modifiers 'shift)) + (unless (listp modifiers) (setq modifiers (list modifiers))) + (global-set-key (vector (append modifiers '(left))) 'windmove-left) + (global-set-key (vector (append modifiers '(right))) 'windmove-right) + (global-set-key (vector (append modifiers '(up))) 'windmove-up) + (global-set-key (vector (append modifiers '(down))) 'windmove-down)) + +;;; Directional window display and selection + +(defcustom windmove-display-no-select nil + "Whether the window should be selected after displaying the buffer in it." + :type 'boolean + :group 'windmove + :version "27.1") + +(defun windmove-display-in-direction (dir &optional arg) + "Display the next buffer in the window at direction DIR. +The next buffer is the buffer displayed by the next command invoked +immediately after this command (ignoring reading from the minibuffer). +Create a new window if there is no window in that direction. +By default, select the window with a displayed buffer. +If prefix ARG is `C-u', reselect a previously selected window. +If `windmove-display-no-select' is non-nil, this command doesn't +select the window with a displayed buffer, and the meaning of +the prefix argument is reversed." + (let* ((no-select (not (eq (consp arg) windmove-display-no-select))) ; xor + (old-window (or (minibuffer-selected-window) (selected-window))) + (new-window) + (minibuffer-depth (minibuffer-depth)) + (action display-buffer-overriding-action) + (command this-command) + (clearfun (make-symbol "clear-display-buffer-overriding-action")) + (exitfun + (lambda () + (setq display-buffer-overriding-action action) + (when (window-live-p (if no-select old-window new-window)) + (select-window (if no-select old-window new-window))) + (remove-hook 'post-command-hook clearfun)))) + (fset clearfun + (lambda () + (unless (or + ;; Remove the hook immediately + ;; after exiting the minibuffer. + (> (minibuffer-depth) minibuffer-depth) + ;; But don't remove immediately after + ;; adding the hook by the same command below. + (eq this-command command)) + (funcall exitfun)))) + (add-hook 'post-command-hook clearfun) + (push (lambda (buffer alist) + (unless (> (minibuffer-depth) minibuffer-depth) + (let ((window (if (eq dir 'same-window) + (selected-window) + (window-in-direction + dir nil nil + (and arg (prefix-numeric-value arg)) + windmove-wrap-around))) + (type 'reuse)) + (unless window + (setq window (split-window nil nil dir) type 'window)) + (setq new-window (window--display-buffer buffer window type alist))))) + display-buffer-overriding-action) + (message "[display-%s]" dir))) + +;;;###autoload +(defun windmove-display-left (&optional arg) + "Display the next buffer in window to the left of the current one. +See the logic of the prefix ARG in `windmove-display-in-direction'." + (interactive "P") + (windmove-display-in-direction 'left arg)) + +;;;###autoload +(defun windmove-display-up (&optional arg) + "Display the next buffer in window above the current one. +See the logic of the prefix ARG in `windmove-display-in-direction'." + (interactive "P") + (windmove-display-in-direction 'up arg)) + +;;;###autoload +(defun windmove-display-right (&optional arg) + "Display the next buffer in window to the right of the current one. +See the logic of the prefix ARG in `windmove-display-in-direction'." + (interactive "P") + (windmove-display-in-direction 'right arg)) + +;;;###autoload +(defun windmove-display-down (&optional arg) + "Display the next buffer in window below the current one. +See the logic of the prefix ARG in `windmove-display-in-direction'." + (interactive "P") + (windmove-display-in-direction 'down arg)) + +;;;###autoload +(defun windmove-display-same-window (&optional arg) + "Display the next buffer in the same window." + (interactive "P") + (windmove-display-in-direction 'same-window arg)) +;;;###autoload +(defun windmove-display-default-keybindings (&optional modifiers) + "Set up keybindings for directional buffer display. +Keys are bound to commands that display the next buffer in the specified +direction. Keybindings are of the form MODIFIERS-{left,right,up,down}, +where MODIFIERS is either a list of modifiers or a single modifier. +Default value of MODIFIERS is `shift-meta'." + (interactive) + (unless modifiers (setq modifiers '(shift meta))) + (unless (listp modifiers) (setq modifiers (list modifiers))) + (global-set-key (vector (append modifiers '(left))) 'windmove-display-left) + (global-set-key (vector (append modifiers '(right))) 'windmove-display-right) + (global-set-key (vector (append modifiers '(up))) 'windmove-display-up) + (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. +When `windmove-wrap-around' is non-nil, takes the window +from the opposite side of the frame." + (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)) + (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) |