branch: externals/window-commander commit c5f85632d970b67a7f4790e02c1c8b2177165a54 Author: Daniel Semyonov <dan...@dsemy.com> Commit: Daniel Semyonov <dan...@dsemy.com>
Rework display functions * swsw.el (swsw-minimum): New user option. (swsw-define-window-command): New macro. (swsw-select, swsw-delete): Redefine using 'swsw-define-window-command'. (swsw-display-buffer-selected-window): New action function for buffer display. (swsw-delete-other, swsw-split-window-below, swsw-split-window-right): (swsw-selected-window-prefix, swsw-swap): New window command. (swsw-select-minibuffer): Simplify slightly. (swsw-command-map): Add bindings for new commands. * README (Usage): * swsw.texi (Usage): Document new commands. (Window commands): Reword the opening paragraph. Document new option and macro. Slightly modify documentation for 'swsw-run-window-command'. * NEWS: Document changes. --- NEWS | 12 +++++ README | 19 +++++-- swsw.el | 178 +++++++++++++++++++++++++++++++++++++++++++++++--------------- swsw.texi | 113 +++++++++++++++++++++++++++------------ 4 files changed, 244 insertions(+), 78 deletions(-) diff --git a/NEWS b/NEWS index b129b4dbdb..ba5ad9074b 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,18 @@ swsw NEWS -- history of user-visible changes. -*- mode: outline -*- See the end of the file for an explanation of the versioning scheme. +* 2.3 + +** Rework window commands +A new macro has been added ('swsw-define-window-command') which +greatly simplifies the creation of window commands; existing commands +now use it and 5 new window commands have been added: +'swsw-delete-other', 'swsw-split-window-below' and +'swsw-split-window-right', which correspond to the built-in commands, +'swsw-selected-window-prefix', which displays the buffer of the next +command in the selected window, and 'swsw-swap' which swaps the state +of the current window and the selected window. + * 2.2 ** Rework display functions diff --git a/README b/README index 64d01da321..3c95819b21 100644 --- a/README +++ b/README @@ -43,12 +43,25 @@ When swsw-mode is active: - Window IDs are assigned to all windows on all frames except for the minibuffer (by default, see ‘swsw-scope’). -C-x o ID switches focus to the window which corresponds to ID. - -C-x o m switches focus to the minibuffer if it's active. +C-x o ID switches focus to the window which corresponds to ID. C-x o 0 ID deletes the window which corresponds to ID. +C-x o 1 ID makes the window which corresponds to ID the sole window of + its frame. + +C-x o 2 ID splits the window which corresponds to ID from below. + +C-x o 3 ID splits the window which corresponds to ID from the right. + +C-x 0 4 ID displays the buffer of the next command in the window which + corresponds to ID. + +C-x 0 t ID swaps the states of the current window and the window which + corresponds to ID. + +C-x o m switches focus to the minibuffer if it's active. + More commands can be added through ‘swsw-command-map’: (define-key swsw-command-map (kbd "z") #'my-command) diff --git a/swsw.el b/swsw.el index d12f350a91..c7f5ce9ac7 100644 --- a/swsw.el +++ b/swsw.el @@ -48,12 +48,25 @@ ;; the minibuffer (by default, see `swsw-scope'). ;; - `other-window' (C-x o by default) is remapped to `swsw-select'. ;; -;; C-x o ID switches focus to the window which corresponds to ID. -;; -;; C-x o m switches focus to the minibuffer if it's active. +;; C-x o ID switches focus to the window which corresponds to ID. ;; ;; C-x o 0 ID deletes the window which corresponds to ID. ;; +;; C-x o 1 ID makes the window which corresponds to ID the sole window of +;; its frame. +;; +;; C-x o 2 ID splits the window which corresponds to ID from below. +;; +;; C-x o 3 ID splits the window which corresponds to ID from the right. +;; +;; C-x 0 4 ID displays the buffer of the next command in the window which +;; corresponds to ID. +;; +;; C-x 0 t ID swaps the states of the current window and the window which +;; corresponds to ID. +;; +;; C-x o m switches focus to the minibuffer if it's active. +;; ;; More commands can be added through `swsw-command-map': ;; ;; (define-key swsw-command-map (kbd "z") #'my-command) @@ -117,6 +130,13 @@ This list should contain at least two characters." :risky t :package-version '(swsw . 1.1)) +(defcustom swsw-minimum 3 + "Minimum number of tracked windows for which interactive selection occurs." + :link '(info-link "(swsw) Window Commands") + :type 'integer + :risky t + :package-version '(swsw . 2.3)) + (define-obsolete-variable-alias 'swsw-display-function 'swsw-display-lighter "version 2.2 of the swsw package" "Function used to display the ID of each window. @@ -327,52 +347,128 @@ exit." (lambda () (run-hooks 'swsw-after-command-hook)))) -(defun swsw-select () - "Start window selection. -If less than three windows have been assigned an ID, switch to the -window returned by `next-window'. -Otherwise, window selection allows either choosing a window by its ID -\(switching to it), or using a window manipulation command. -This command is intended to be used only when swsw mode is enabled." - (declare (modes swsw-mode) - (interactive-only t)) - (interactive) - (if (< swsw-window-count 3) - (select-window (next-window nil nil (swsw--get-scope))) - (swsw-run-window-command #'select-window))) +(defmacro swsw-define-window-command (name args &rest body) + "Define NAME as a window command with DOCSTRING as its documentation string. + +Inside BODY, WINDOW and PREFIX (symbols) are bound to the selected +window and the raw prefix argument, respectively. +If PREFIX is omitted or nil, the resulting command will not accept a +prefix argument. + +If MINIBUFFER is non-nil, allow the minibuffer to be selected by +`next-window' (when there are less than `swsw-minimum' tracked windows). + +For more information, see info node `(swsw) Window Commands'. + +\(fn NAME (WINDOW [PREFIX] [MINIBUFFER]) [DOCSTRING] BODY...)" + (declare (debug (&define [&name symbolp] listp [&optional stringp] def-body)) + (doc-string 3) (indent defun)) + (let* ((docstring (car body)) (window (car args)) + (prefix (cadr args)) (minibuffer (caddr args))) + `(defun ,name ,(and prefix `(,prefix)) + ,(when (stringp docstring) (format "%s + +If less than `swsw-minimum' windows have been assigned an ID, +use the window returned by `next-window' (according to the +value of `swsw-scope'%s). +Otherwise, either a window is selected using its ID or a separate +window command is chosen. + + This is a window command, intended to be used only when simple + window switching is enabled; for more information, see info node + `(swsw) Window Commands'. +" docstring (if minibuffer "" ", excluding the minibuffer"))) + (declare (modes swsw-mode) + (interactive-only t)) + (interactive ,(and prefix "P")) + (if-let ((f (lambda (,window) + ,@body)) + ((>= swsw-window-count swsw-minimum))) + (swsw-run-window-command f) + (funcall f (next-window nil (unless ,minibuffer 'exclude) + (swsw--get-scope))))))) + +(swsw-define-window-command swsw-select (window nil t) + "Select a window." + (select-window window)) + +(swsw-define-window-command swsw-delete (window) + "Delete a window." + (delete-window window)) + +(swsw-define-window-command swsw-delete-other (window) + "Make a window the sole window of its frame." + (delete-other-windows window)) + +(swsw-define-window-command swsw-split-window-below (window size) + "Split a window from below. +If optional argument SIZE is omitted or nil, both windows get the same +height, or close to it. If SIZE is positive, the upper window gets +SIZE lines. If SIZE is negative, the lower window gets -SIZE lines." + (split-window-below (and size (prefix-numeric-value size)) window)) + +(swsw-define-window-command swsw-split-window-right (window size) + "Split a window from the right. +If optional argument SIZE is omitted or nil, both windows get the same +width, or close to it. If SIZE is positive, the left-hand window gets +SIZE columns. If SIZE is negative, the right-hand window gets -SIZE +columns. Here, SIZE includes the width of the window’s scroll bar; if +there are no scroll bars, it includes the width of the divider column +to the window’s right, if any." + (split-window-right (and size (prefix-numeric-value size)) window)) + +(defun swsw-display-buffer-selected-window (buffer alist) + "Display BUFFER in the selected (through swsw) window. +ALIST is an association list of action symbols and values. See +Info node `(elisp) Buffer Display Action Alists' for details of +such alists. + +The function fails if ALIST has no `window' element or its value isn't a +live window, or if it is a minibuffer window or is dedicated to another +buffer; in that case return nil. +Otherwise, return the value of the `window' element. + +This is an action function for buffer display, see Info +node ‘(elisp) Buffer Display Action Functions’. It should be +called only by ‘display-buffer’ or a function directly or +indirectly called by the latter." + (let ((window (cdr (assq 'window alist)))) + (unless (or (not (windowp window)) + (window-minibuffer-p window) + (window-dedicated-p window)) + (window--display-buffer buffer window 'reuse alist)))) + +(swsw-define-window-command swsw-selected-window-prefix (window) + "Display the buffer of the next command in a window." + (display-buffer-override-next-command + (lambda (buffer alist) + (setq alist (append `((window . ,window)) alist)) + (cons (swsw-display-buffer-selected-window buffer alist) 'reuse)) + nil (format "[swsw-window-%s]" (window-parameter window 'swsw-id))) + (message "Display next command buffer in the selected window...")) + +(swsw-define-window-command swsw-swap (window) + "Swap the states of a window and the currently selected window." + (window-swap-states nil window) + (and (eq (current-buffer) (window-buffer window)) (swsw--update))) (defun swsw-select-minibuffer () - "Select the active minibuffer window (if it exists). -This command is intended to be used only when swsw mode is enabled." - (declare (modes swsw-mode) - (interactive-only t)) + "Select the active minibuffer window (if it exists)." + (declare (modes swsw-mode)) (interactive) - (if-let ((window (active-minibuffer-window))) - (select-window window) - (user-error "There is no active minibuffer window"))) - -(defun swsw-delete () - "Start window deletion. -If less than three windows have been assigned an ID, delete the window -returned by `next-window'. -Otherwise, window deletion allows either choosing a window by its ID -\(deleting it), or using a window manipulation command. -This command is intended to be used only when swsw mode is enabled." - (declare (modes swsw-mode) - (interactive-only t)) - (interactive) - (if-let (((< swsw-window-count 3)) - (window (next-window nil nil (swsw--get-scope)))) - (unless (or (minibufferp (window-buffer window)) - (minibufferp)) ; Selected window. - (delete-window window)) - (swsw-run-window-command #'delete-window))) + (select-window (or (active-minibuffer-window) + (user-error "There is no active minibuffer window")))) (defvar swsw-command-map (let ((map (make-sparse-keymap))) (define-key map [?o] #'swsw-select) - (define-key map [?m] #'swsw-select-minibuffer) (define-key map [?0] #'swsw-delete) + (define-key map [?1] #'swsw-delete-other) + (define-key map [?2] #'swsw-split-window-below) + (define-key map [?3] #'swsw-split-window-right) + (define-key map [?4] #'swsw-selected-window-prefix) + (define-key map [?t] #'swsw-swap) + (define-key map [?m] #'swsw-select-minibuffer) map) "Key map for window commands. This key map is set as the parent of `swsw--id-map' during ID diff --git a/swsw.texi b/swsw.texi index b6720058f8..7ed876c76c 100644 --- a/swsw.texi +++ b/swsw.texi @@ -142,28 +142,53 @@ lighters of the form @code{<ID>} (by default, @xref{ID display}), and @table @asis @kindex C-x o +@kindex C-x o o @cindex swsw-select -@item @kbd{C-x o} -Start window selection (@code{swsw-select}). -This command sets a transient key map in which several window -commands are defined, and window IDs can be selected. -Selecting a window ID (by pressing @kbd{ID}) will switch to the window -corresponding to it. +@item @kbd{C-x o ID}, @kbd{C-x o o ID} +Select the window corresponding to ID in the current scope +(@xref{Customization}) (@code{swsw-select}). + +@kindex C-x o 0 +@cindex swsw-delete +@item @kbd{C-x o 0 ID} +Delete the window corresponding to ID in the current scope (@code{swsw-delete}). + +@kindex C-x o 1 +@cindex swsw-delete-other +@item @kbd{C-x o 1 ID} +Make the window corresponding to ID the in the current scope the sole +window of its frame (@code{swsw-delete-other}). + +@kindex C-x o 2 +@cindex swsw-split-window-below +@item @kbd{C-x o 2 ID} +Split the window corresponding to ID in the current scope from below +(@code{swsw-split-window-below}). This window command accepts a +prefix argument (with the same semantics as @code{split-window-below}). + +@kindex C-x o 3 +@cindex swsw-split-window-right +@item @kbd{C-x o 3 ID} +Split the window corresponding to ID in the current scope from the right +(@code{swsw-split-window-right}). This window command accepts a +prefix argument (with the same semantics as @code{split-window-right}). + +@kindex C-x o 4 +@cindex swsw-selected-window-prefix +@item @kbd{C-x o 4 ID} +Display the buffer of the next command in the window corresponding to +ID in the current scope (@code{swsw-delete}). + +@kindex C-x o t +@cindex swsw-swap +@item @kbd{C-x o t ID} +Swap the states of the current window and the window corresponding to +ID in the current scope (@code{swsw-delete}). @kindex C-x o m @cindex swsw-select-minibuffer @item @kbd{C-x o m} -Switch focus the minibuffer if it's active -(@code{swsw-select-minibuffer}). - -@kindex C-x o 0 -@cindex swsw-delete -@item @kbd{C-x o 0} -Start window deletion (@code{swsw-delete}). -This command sets a transient key map in which several window -commands are defined, and window IDs can be selected. -Selecting a window ID (by pressing @kbd{ID}) will delete the window -corresponding to it. +Switch to the minibuffer if it's active (@code{swsw-select-minibuffer}). @end table @xref{Window commands} for information regarding defining new commands. @@ -262,18 +287,24 @@ display function respects @code{swsw-id-format}. @node Window commands @section Window commands -Window commands are used to perform operations on specific -windows. They are normal commands (interactive functions) which are -bound to a key sequence in @code{swsw-command-map}. @code{swsw-select}, -@code{swsw-select-minibuffer} and @code{swsw-delete} are implemented -as window commands (@xref{Usage}). - -@code{swsw-run-window-command} should be used to define window -commands which require window selection. Alternatively, ensure -@code{swsw-before-command-hook} runs before window selection, and -@code{swsw-after-command-hook} runs after window selection. Window -selection can be started by setting @code{swsw--id-map} as a transient -map. +Window commands are used to perform operations on specific windows, +chosen by their ID (or the next window, if less than +@code{swsw-minimum} (3 by default) windows are currently in scope). +Alternatively, other commands available in @code{swsw-command-map} can +be chosen. Not all commands in @code{swsw-command-map} are window +commands (by default). For more information about included window +commands, @xref{Usage}. + +Window commands can easily be defined using +@code{swsw-define-window-command}. For more complex use cases, +the lower level @code{swsw-run-window-command} can be used. + +@defopt swsw-minimum +Minimum number of tracked windows for which interactive selection +occurs when using window commands. In practice, only window commands +defined using @code{swsw-define-window-command} automatically adhere +to this rule. +@end defopt @defvar swsw--id-map Key map which is populated automatically with elements corresponding @@ -296,14 +327,28 @@ active). It can be used to change the behavior of window commands (or display functions, @xref{ID display}). @end defvar +@defmac swsw-define-window-command name (window [prefix] [minibuffer]) docstring body... +Define NAME as a window command with DOCSTRING as its documentation +string. PREFIX (a symbol) is passed as an argument to the command +(when not omitted or nil), with it's value being the raw prefix +argument. + +BODY is used as the body of an anonymous function which receives +WINDOW (a symbol) as an argument, with its value being a selected +window. If there are less than @code{swsw-minimum} (3 by default) +windows in the current scope (@xref{Customization}), the function is +called with the window returned by @code{next-window}. In this case, +if MINIBUFFER is non-nil, it can be selected if it's active. +Otherwise, the function is run using @code{swsw-run-window-command}. +@end defmac + @defun swsw-run-window-command fun Run FUN as a window command. Run @code{swsw-before-command-hook}, set @code{this-command} to FUN, and set @code{swsw--id-map} as a transient -map which runs @code{swsw-after-command-hook} on exit. This function -can be used to easily define new window commands. The hooks ran by -this function are expected by swsw to run for any window command which -requires ID selection; they should be manually added even if this -function isn't used when defining a new window command. +map which runs @code{swsw-after-command-hook} on exit. The hooks run +by this function are expected by swsw to run for any window command +which requires ID selection; they should be manually added even if +this function isn't used when defining a new window command. @end defun @node Keystroke Index