Compare commits

..

3 commits

Author SHA1 Message Date
Kiana Sheibani aca0f74f3a
chore: bump version number 2024-08-26 19:22:29 -04:00
Kiana Sheibani a23813c7ca
docs: update README for new features 2024-08-26 19:18:59 -04:00
Kiana Sheibani e0c329e0f6
feat!: support for arbitrary objects as indices
BREAKING-CHANGE: Many external and internal functions have been renamed
to replace the word "number" with the more general word "index."
2024-08-26 19:04:33 -04:00
2 changed files with 250 additions and 229 deletions

View file

@ -5,6 +5,7 @@
* Contents :TOC:noexport:
- [[#introduction][Introduction]]
- [[#changelog][Changelog]]
- [[#aug-26-2024][Aug. 26 2024]]
- [[#sep-11-2019][Sep. 11 2019]]
- [[#nov-15-2018][Nov. 15 2018]]
- [[#initial-release-whats-changed-since-window-numbering][Initial release: what's changed since window-numbering]]
@ -19,16 +20,18 @@
- [[#future-developments][Future developments]]
* Introduction
Window numbers for Emacs: Navigate your windows and frames using numbers !
This package is an extended and actively maintained version of the
[[https://github.com/nschum/window-numbering.el][window-numbering]] package by Nikolaj Schumacher, with some ideas and code taken
from [[https://github.com/abo-abo/ace-window][ace-window]].
This package is a fork of the [[https://github.com/deb0ch/emacs-winum][winum]] package, which is itself a fork of the
[[https://github.com/nschum/window-numbering.el][window-numbering]] package.
This version brings, among other things, support for number sets across multiple
frames, giving the user a smoother experience of multi-screen Emacs.
Compared to winum, this fork adds support for windows having arbitrary Lisp values
as identifiers, as opposed to strictly increasing numbers. The public
API is largely the same, however, and simple numbers are still the default.
* Changelog
** Aug. 26 2024
- Added support for arbitrary Lisp objects (non-nil, compared with =equal=) as indices.
- Added customize variable =winum-auto-assign-function= to control auto-assigning behavior.
** Sep. 11 2019
- Added customize variable =winum-ignored-buffers-regexp= to ignored buffers
based on regexps.
@ -101,7 +104,7 @@ your Emacs configuration:
| Key binding | Description |
|-------------+---------------------------------------------------------------------------------------------|
| ~C-x w <n>~ | select window <n>, where <n> ranges from 0 to 9. A negative argument deletes the window. |
| ~C-x w `~ | select window by number. Number can be given as prefix arg or will be read from minibuffer. |
| ~C-x w `~ | select window by index. Integer indices can be given as prefix arg, or the index will be read from minibuffer. |
- =select-window-0-or-10=
@ -111,23 +114,19 @@ your Emacs configuration:
You can rebind this to the more straightforward =select-window-0= if you
prefer.
- =select-window-by-number=
- =select-window-by-index=
If you happen to have more than 10 windows, you can use the
=select-window-by-number= function, bound by default to ~C-x w `~.
If you happen to have more than 10 windows, or if you want to select a window that
doesn't have an integer index, you can use the =select-window-by-index= function, bound
by default to ~C-x w `~.
This function allows several ways to input the window number:
- Use a numbered prefix argument.\\
*Ex:* ~C-1 C-2 C-x w `~ to select window 12.
- Use a negative prefix argument to delete the window.\\
*Ex:* ~C-- C-1 C-2 C-x w `~ to delete window 12.
- Use the negative prefix argument to delete window 0.\\
*Ex:* ~C-- C-x w `~ to delete window 0.
- Use the default prefix argument to delete current window.\\
*Ex:* ~C-u C-x w `~ to delete current window.
- If no prefix argument ig given, a number is read from minibuffer. A negative
input will delete the window instead of selecting it.
- If no prefix argument is given, an un-evaluated Lisp value is read from minibuffer.
* Configuration
** Keybindings
@ -152,8 +151,8 @@ override =winum-keymap= in the minor mode bindings table:
#+BEGIN_SRC emacs-lisp
(setq winum-keymap
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-`") 'winum-select-window-by-number)
(define-key map (kbd "C-²") 'winum-select-window-by-number)
(define-key map (kbd "C-`") 'winum-select-window-by-index)
(define-key map (kbd "C-²") 'winum-select-window-by-index)
(define-key map (kbd "M-0") 'winum-select-window-0-or-10)
(define-key map (kbd "M-1") 'winum-select-window-1)
(define-key map (kbd "M-2") 'winum-select-window-2)
@ -175,7 +174,7 @@ Note that it is important to set =winum-keymap= /before/ the =require=.
You can also use the more conventional =define-key= on =winum-keymap=:
#+BEGIN_SRC emacs-lisp
(define-key winum-keymap (kbd "C-x y o l o") 'winum-select-window-by-number)
(define-key winum-keymap (kbd "C-x y o l o") 'winum-select-window-by-index)
#+END_SRC
*NB:* Both ~`~ and ~²~ are mapped to =winum-select-window-by-number= by default
@ -201,11 +200,11 @@ Several options are available through Emacs' Customize interface under
Default: =nil=
- =winum-auto-assign-0-to-minibuffer=
- =winum-minibuffer-auto-assign=
If non-nil, =winum-mode= assigns 0 to the minibuffer if active.
If non-nil, =winum-mode= automatically assigns this index to the minibuffer.
Default: =t=
Default: =0=
- =winum-assign-functions=
@ -244,6 +243,18 @@ Several options are available through Emacs' Customize interface under
Default: =nil=
- =winum-auto-assign-function=
The function called to auto-assign indices to windows.
This function is called after =winum-assign-functions= to automatically assign
indices to the remaining windows. It must take in a list of windows and call
the function =winum--assign= to assign an index to each, while avoiding
assigning any indices already taken (stored in =winum--assigned-indices=).
Members of this list should be tested for using `equal'.
Default: =#'winum--auto-assign=
- =winum-auto-setup-mode-line=
When nil, =winum-mode= will not display window numbers in the mode-line. You
@ -290,8 +301,8 @@ available winum options.
#+BEGIN_SRC emacs-lisp
(setq winum-keymap
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-`") 'winum-select-window-by-number)
(define-key map (kbd "C-²") 'winum-select-window-by-number)
(define-key map (kbd "C-`") 'winum-select-window-by-index)
(define-key map (kbd "C-²") 'winum-select-window-by-index)
(define-key map (kbd "M-0") 'winum-select-window-0-or-10)
(define-key map (kbd "M-1") 'winum-select-window-1)
(define-key map (kbd "M-2") 'winum-select-window-2)
@ -320,7 +331,7 @@ available winum options.
(setq window-numbering-scope 'global
winum-reverse-frame-list nil
winum-auto-assign-0-to-minibuffer t
winum-minibuffer-auto-assign 0
winum-assign-func 'my-winum-assign-func
winum-auto-setup-mode-line t
winum-format " %s "

418
winum.el
View file

@ -1,7 +1,8 @@
;;; winum.el --- Navigate windows and frames using numbers.
;;
;; Copyright (c) 2006-2015 Nikolaj Schumacher
;; Copyright (c) 2016 Thomas Chauvot de Beauchêne
;; Copyright (c) 2016-2019 Thomas Chauvot de Beauchêne
;; Copyright (c) 2024 Kiana Sheibani
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
@ -16,8 +17,8 @@
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;
;; Author: Thomas de Beauchêne <thomas.de.beauchene@gmail.com>
;; Version: 2.2.0
;; Author: Kiana Sheibani <kiana.a.sheibani@gmail.com>
;; Version: 3.0.0
;; Keywords: convenience, frames, windows, multi-screen
;; URL: http://github.com/deb0ch/winum.el
;; Created: 2016
@ -38,9 +39,6 @@
;; frames, giving the user a smoother experience of multi-screen Emacs.
;;
;;; Code:
;;
;; FIXME: The mode-line's window number is not always up to date in all frames.
;;
(eval-when-compile (require 'cl-lib))
(require 'dash)
@ -65,18 +63,15 @@ Has effect only when `winum-scope' is not 'frame-local."
:group 'winum
:type 'boolean)
(defcustom winum-auto-assign-0-to-minibuffer t
"If non-nil, `winum-mode' assigns 0 to the minibuffer when active."
(defcustom winum-minibuffer-auto-assign 0
"If non-nil, `winum-mode' automatically assigns this index to the minibuffer."
:group 'winum
:type 'boolean)
:type 'sexp)
(defcustom winum-assign-func nil
"Function called for each window by `winum-mode'.
This is called before automatic assignment begins. The function should
return a number to have it assigned to the current-window, nil otherwise.
This function along with `winum-auto-assign-0-to-minibuffer' are the only
ways to have 0 assigned to a window.
return an index to have it assigned to the current-window, nil otherwise.
Example: always assign *Calculator* the number 9 and *NeoTree* the number 0:
@ -98,21 +93,18 @@ Example: always assign *Calculator* the number 9 and *NeoTree* the number 0:
(defcustom winum-assign-functions nil
"List of functions called for each window by `winum-mode'.
These functions allow for deterministic assignment of numbers to windows. Each
function is called for every window. A function should return the number to be
assigned to a window or nil. The *first* function to output a number for
a given window will determine this window's number.
These functions allow for deterministic assignment of indices to windows. Each
function is called for every window. A function should return the index to be
assigned to a window or nil. The *first* function to output a value for a
given window will determine this window's number.
If the list is empty or if every functions returns nil for a given window winum
will proceed to automatic number assignment.
will proceed to automatic assignment.
Since this list is meant to allow custom window assignment for *mutiple*
packages at once it should never be directly set, only added to and removed
from.
These functions, along with `winum-auto-assign-0-to-minibuffer', are the only
way to have 0 assigned to a window.
Example: always assign *Calculator* the number 9, *Flycheck-errors* the number 8
and *NeoTree* the number 0:
@ -129,7 +121,21 @@ and *NeoTree* the number 0:
(add-to-list
'winum-assign-functions #'winum-assign-0-to-neotree)"
:group 'winum
:type 'list)
:type '(repeat function))
(defcustom winum-auto-assign-function #'winum--auto-assign
"The function called to auto-assign indices to windows.
This function is called after `winum-assign-functions' to automatically assign
indices to the remaining windows. It must take in a list of windows and call
the function `winum--assign' to assign an index to each, while avoiding
assigning any indices already taken (stored in `winum--assigned-indices').
Members of this list should be tested for using `equal'.
The default auto-assign function is `winum--auto-assign', which assigns strictly
increasing integers to each window."
:group 'winum
:type 'function)
(defcustom winum-auto-setup-mode-line t
"When nil, `winum-mode' will not display window numbers in the mode-line.
@ -143,33 +149,38 @@ numbers in the mode-line."
:group 'winum
:type 'integer)
(defcustom winum-mode-line-p #'integerp
"A predicate that determines when to display a window index on the mode-line.
By default, only integers are displayed."
:group 'winum
:type 'function)
(defcustom winum-format " %s "
"Format string defining how the window number looks like in the mode-line.
This string is passed to the `format' function along with the
result of `winum-get-number-string'."
This string is passed to the `format' function along with the index."
:group 'winum
:type 'string)
(defcustom winum-ignored-buffers '(" *which-key*")
"List of buffers to ignore when assigning numbers."
"List of buffers to ignore when assigning indices."
:group 'winum
:type '(repeat string))
(defcustom winum-ignored-buffers-regexp '()
"List of regexps for buffer names to ignore when assigning numbers.
"List of regexps for buffer names to ignore when assigning indices.
See Info node `(emacs) Regexps' or Info node `(elisp) Regular Expressions'"
:group 'winum
:type '(repeat string)
:risky t)
(defface winum-face '()
"Face used for the number in the mode-line."
"Face used for the index in the mode-line."
:group 'winum)
(defvar winum-base-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "`") 'winum-select-window-by-number)
(define-key map (kbd "²") 'winum-select-window-by-number)
(define-key map (kbd "`") 'winum-select-window-by-index)
(define-key map (kbd "²") 'winum-select-window-by-index)
(define-key map (kbd "0") 'winum-select-window-0-or-10)
(define-key map (kbd "1") 'winum-select-window-1)
(define-key map (kbd "2") 'winum-select-window-2)
@ -183,9 +194,9 @@ See Info node `(emacs) Regexps' or Info node `(elisp) Regular Expressions'"
map)
"Keymap to be used under the prefix provided by `winum-keymap-prefix'.")
(defvar winum-keymap (let ((map (make-sparse-keymap)))
(define-key map (kbd "C-x w") winum-base-map)
map)
(defvar winum-mode-map (let ((map (make-sparse-keymap)))
(define-key map (kbd "C-x w") winum-base-map)
map)
"Keymap used for `winum-mode'.")
;; Internal variables ----------------------------------------------------------
@ -193,37 +204,36 @@ See Info node `(emacs) Regexps' or Info node `(elisp) Regular Expressions'"
(defvar winum--max-frames 16
"Maximum number of frames that can be numbered.")
(defvar winum--window-count nil
"Current count of windows to be numbered.")
(defvar winum--assigned-indices nil
"List of indices that have already been assigned by `winum-assign-functions'.")
(defvar winum--remaining nil
"A list of window numbers to assign.")
(defvar winum--window-table nil
"Hash table with indices as keys and windows as values.
Used internally by winum to get a window provided an index.")
(defvar winum--window-vector nil
"Vector of windows indexed by their number.
Used internally by winum to get a window provided a number.")
(defvar winum--numbers-table nil
"Hash table of numbers indexed by their window.
Used internally by winum to get a number provided a window.")
(defvar winum--index-table nil
"Hash table with windows as keys and indices as values.
Used internally by winum to get an index provided a window.")
(defvar winum--frames-table nil
"Table linking windows to numbers and numbers to windows for each frame.
"Table linking windows to indices and indices to windows for each frame.
Used only when `winum-scope' is 'frame-local to keep track of
separate window numbers sets in every frame.
separate window index sets in every frame.
It is a hash table using Emacs frames as keys and cons of the form
\(`winum--window-vector' . `winum--numbers-table')
\(`winum--window-table' . `winum--index-table')
as values.
To get a window given a number, use the `car' of a value.
To get a number given a window, use the `cdr' of a value.
To get a window given an index, use the `car' of a value.
To get an index given a window, use the `cdr' of a value.
Such a structure allows for per-frame bidirectional fast access.")
(defvar winum--mode-line-segment
'(:eval (format winum-format (winum-get-number-string)))
'(:eval (let ((index (winum-get-index)))
(when (funcall winum-mode-line-p index)
(propertize (format winum-format index) 'face 'winum-face))))
"What is pushed into `mode-line-format' when setting it up automatically.")
(defvar winum--last-used-scope winum-scope
@ -235,9 +245,7 @@ Needed to detect scope changes at runtime.")
;;;###autoload
(define-minor-mode winum-mode
"A minor mode that allows for managing windows based on window numbers."
nil
nil
winum-keymap
:lighter nil
:global t
(if winum-mode
(winum--init)
@ -245,115 +253,145 @@ Needed to detect scope changes at runtime.")
;;;###autoload
(defun winum-select-window-0-or-10 (&optional arg)
"Jump to window 0 if assigned or 10 if exists.
"Jump to window 0, or window 10 if 0 is not assigned.
If prefix ARG is given, delete the window instead of selecting it."
(interactive "P")
(let ((n (if (winum-get-window-by-number 0)
(if arg '- 0)
(if arg -10 10))))
(winum-select-window-by-number n)))
(let ((n (if (winum-get-window-by-index 0) 0 10)))
(if arg
(winum-delete-window-by-index n)
(winum-select-window-by-index n))))
;;;###autoload
(defun winum-select-window-0 (&optional arg)
"Jump to window 0.
If prefix ARG is given, delete the window instead of selecting it."
(interactive "P")
(winum-select-window-by-number (if arg '- 0)))
(if arg
(winum-delete-window-by-index 0)
(winum-select-window-by-index 0)))
;;;###autoload
(defun winum-select-window-1 (&optional arg)
"Jump to window 1.
If prefix ARG is given, delete the window instead of selecting it."
(interactive "P")
(winum-select-window-by-number (if arg -1 1)))
(if arg
(winum-delete-window-by-index 1)
(winum-select-window-by-index 1)))
;;;###autoload
(defun winum-select-window-2 (&optional arg)
"Jump to window 2.
If prefix ARG is given, delete the window instead of selecting it."
(interactive "P")
(winum-select-window-by-number (if arg -2 2)))
(if arg
(winum-delete-window-by-index 2)
(winum-select-window-by-index 2)))
;;;###autoload
(defun winum-select-window-3 (&optional arg)
"Jump to window 3.
If prefix ARG is given, delete the window instead of selecting it."
(interactive "P")
(winum-select-window-by-number (if arg -3 3)))
(if arg
(winum-delete-window-by-index 3)
(winum-select-window-by-index 3)))
;;;###autoload
(defun winum-select-window-4 (&optional arg)
"Jump to window 4.
If prefix ARG is given, delete the window instead of selecting it."
(interactive "P")
(winum-select-window-by-number (if arg -4 4)))
(if arg
(winum-delete-window-by-index 4)
(winum-select-window-by-index 4)))
;;;###autoload
(defun winum-select-window-5 (&optional arg)
"Jump to window 5.
If prefix ARG is given, delete the window instead of selecting it."
(interactive "P")
(winum-select-window-by-number (if arg -5 5)))
(if arg
(winum-delete-window-by-index 5)
(winum-select-window-by-index 5)))
;;;###autoload
(defun winum-select-window-6 (&optional arg)
"Jump to window 6.
If prefix ARG is given, delete the window instead of selecting it."
(interactive "P")
(winum-select-window-by-number (if arg -6 6)))
(if arg
(winum-delete-window-by-index 6)
(winum-select-window-by-index 6)))
;;;###autoload
(defun winum-select-window-7 (&optional arg)
"Jump to window 7.
If prefix ARG is given, delete the window instead of selecting it."
(interactive "P")
(winum-select-window-by-number (if arg -7 7)))
(if arg
(winum-delete-window-by-index 7)
(winum-select-window-by-index 7)))
;;;###autoload
(defun winum-select-window-8 (&optional arg)
"Jump to window 8.
If prefix ARG is given, delete the window instead of selecting it."
(interactive "P")
(winum-select-window-by-number (if arg -8 8)))
(if arg
(winum-delete-window-by-index 8)
(winum-select-window-by-index 8)))
;;;###autoload
(defun winum-select-window-9 (&optional arg)
"Jump to window 9.
If prefix ARG is given, delete the window instead of selecting it."
(interactive "P")
(winum-select-window-by-number (if arg -9 9)))
(if arg
(winum-delete-window-by-index 9)
(winum-select-window-by-index 9)))
;;;###autoload
(defun winum-select-window-by-number (&optional arg)
"Select or delete window which number is specified by ARG.
If the number is negative, delete the window instead of selecting it.
There are several ways to provide the number:
(defun winum-select-window-by-index (&optional arg)
"Select window whose index is specified by ARG.
There are several ways to provide the value:
- if called from elisp with an argument, use it.
- if called from elisp without an argument, use current window.
- if called interactively with a numeric prefix argument, use it.
- if prefix argument is the negative argument, delete window 0.
- if prefix argument is the default prefix argument, delete current window.
- if called interactively and no valid argument is provided, read from
minibuffer."
- if called interactively with the default prefix argument, use current window.
- if called interactively and no argument is provided, read from minibuffer."
(interactive "P")
(let* ((n (cond
((integerp arg) arg)
((eq arg '-) 0) ; the negative argument
(arg (winum-get-number))
((called-interactively-p 'any)
(let ((user-input-str (read-from-minibuffer "Window: ")))
(if (not (string-match-p "[+-]?[0-9]+\.*" user-input-str))
(winum-get-number)
(string-to-number user-input-str))))
(t (winum-get-number))))
(w (winum-get-window-by-number (abs n)))
(delete (and arg
(or (not (integerp arg))
(> 0 n)))))
(let* ((i (if (called-interactively-p 'any)
(cond
((integerp arg) arg)
(arg (winum-get-index))
(t (read--expression "Window: ")))
(or arg (winum-get-index))))
(w (winum-get-window-by-index i)))
(if w
(if delete
(delete-window w)
(winum--switch-to-window w))
(error "No window numbered %d" n))))
(winum--switch-to-window w)
(user-error "No window with index %S" i))))
;;;###autoload
(defun winum-delete-window-by-index (&optional arg)
"Delete window whose index is specified by ARG.
There are several ways to provide the value:
- if called from elisp with an argument, use it.
- if called from elisp without an argument, use current window.
- if called interactively with a numeric prefix argument, use it.
- if called interactively with the default prefix argument, use current window.
- if called interactively and no argument is provided, read from minibuffer."
(interactive "P")
(let* ((i (if (called-interactively-p 'any)
(cond
((integerp arg) arg)
(arg (winum-get-index))
(t (read--expression "Window: ")))
(or arg (winum-get-index))))
(w (winum-get-window-by-index i)))
(if w
(delete-window w)
(user-error "No window with index %S" i))))
;; Public API ------------------------------------------------------------------
@ -371,41 +409,38 @@ PREFIX must be a key sequence, like the ones returned by `kbd'."
winum-keymap))
;;;###autoload
(defun winum-get-window-by-number (n)
"Return window numbered N if exists, nil otherwise."
(let ((window-vector (winum--get-window-vector)))
(when (and (>= n 0) (< n (length window-vector)))
(aref window-vector n))))
(defun winum-get-window-by-index (index)
"Return window INDEX if exists, nil otherwise."
(let ((window-table (winum--get-window-table)))
(gethash index window-table)))
;;;###autoload
(defun winum-get-index (&optional window)
"Get the index of WINDOW or the current window."
(let ((w (or window (selected-window))))
(gethash w (winum--get-index-table))))
;; For backwards compatibility
;;;###autoload
(defun winum-get-number-string (&optional window)
"Get the current or specified window's current number as a propertized string.
WINDOW: if specified, the window of which we want to know the number.
If not specified, the number of the currently selected window is
returned."
(let* ((n (winum-get-number window))
(s (if (numberp n)
(int-to-string n)
(let* ((n (winum-get-index window))
(s (if (funcall winum-mode-line-p n)
(format "%s" n)
"")))
(propertize s 'face 'winum-face)))
;;;###autoload
(defun winum-get-number (&optional window)
"Get the current or specified window's current number.
WINDOW: if specified, the window of which we want to know the number.
If not specified, the number of the currently selected window is
returned."
(let ((w (or window (selected-window))))
(gethash w (winum--get-numbers-table))))
;; Internal functions ----------------------------------------------------------
(defun winum--init ()
"Initialize winum-mode."
(setq winum--window-count (length (winum--window-list)))
(if (eq winum-scope 'frame-local)
(setq winum--frames-table (make-hash-table :size winum--max-frames))
(setq winum--numbers-table (make-hash-table :size winum--window-count)))
(setq winum--window-table (make-hash-table :test 'equal)
winum--index-table (make-hash-table :test 'equal)))
(when winum-auto-setup-mode-line
(winum--install-mode-line))
(add-hook 'minibuffer-setup-hook 'winum--update)
@ -423,7 +458,7 @@ WINDOW: if specified, the window of which we want to know the number.
(setq winum--frames-table nil))
(defun winum--install-mode-line (&optional position)
"Install the window number from `winum-mode' to the mode-line.
"Install the window index from `winum-mode' to the mode-line.
POSITION: position in the mode-line."
(let ((mode-line (default-value 'mode-line-format))
res)
@ -440,84 +475,76 @@ POSITION: position in the mode-line."
(force-mode-line-update t))
(defun winum--clear-mode-line ()
"Remove the window number of `winum-mode' from the mode-line."
"Remove the window index of `winum-mode' from the mode-line."
(let ((mode-line (default-value 'mode-line-format))
res)
(while mode-line
(let ((item (pop mode-line)))
(unless (equal item winum--mode-line-segment)
(push item res))))
(push item res))))
(let ((nres (nreverse res)))
(setq mode-line-format nres)
(setq-default mode-line-format nres)))
(force-mode-line-update t))
(defun winum--update ()
"Update window numbers."
"Update window indices."
(let ((windows (winum--window-list)))
(setq winum--window-count (length windows)
winum--remaining (winum--available-numbers))
(winum--set-window-vector (make-vector (1+ winum--window-count) nil))
(clrhash (winum--get-numbers-table))
(setq winum--assigned-indices nil)
(clrhash (winum--get-window-table))
(clrhash (winum--get-index-table))
(when winum-assign-functions
(-each windows #'winum--try-to-find-custom-number))
(when (and winum-auto-assign-0-to-minibuffer
(--each (-copy windows) (when (winum--try-to-find-custom-index it)
(setq windows (delq it windows)))))
(when (and winum-minibuffer-auto-assign
(active-minibuffer-window)
(not (winum-get-window-by-number 0)))
(winum--assign (active-minibuffer-window) 0))
(dolist (w windows)
(winum--assign w))))
(not (winum-get-window-by-index winum-minibuffer-auto-assign)))
(winum--assign (active-minibuffer-window) winum-minibuffer-auto-assign)
(push winum-minibuffer-auto-assign winum--assigned-indices))
;; Auto-assign remaining windows
(when windows
(funcall winum-auto-assign-function windows))))
(defun winum--try-to-find-custom-number (window)
"Try to find and assign a custom number for WINDOW.
(defun winum--auto-assign (windows)
"The default auto-assign function for assigning indices to windows.
Takes in the list of windows WINDOWS and uses `winum--assign' to assign
increasing integers to it."
(let ((index 1))
(dolist (w windows)
(while (member index winum--assigned-indices)
(setq index (1+ index)))
(winum--assign w index)
(setq index (1+ index)))))
(defun winum--try-to-find-custom-index (window)
"Try to find and assign a custom index for WINDOW.
Do so by trying every function in `winum-assign-functions' and assign the
*first* non nil integer.
When multiple functions assign a number to a window log a warning and use the
first number anyway."
*first* non nil value.
When multiple functions assign an index to a window log a warning and use the
first index anyway."
(with-selected-window window
(with-current-buffer (window-buffer window)
(let* ((nums (->> winum-assign-functions
(--map (cons it (funcall it)))
(--remove (null (cdr it)))))
(num (-> nums (cl-first) (cdr))))
(when (> (length nums) 1)
(message "Winum conflict - window %s was assigned a number by multiple custom assign functions: '%s'"
window (--map (format "%s -> %s" (car it) (cdr it)) nums)))
(when (integerp num) (winum--assign window num))))))
(when-let* ((inds (->> winum-assign-functions
(--map (cons it (funcall it)))
(--remove (null (cdr it)))))
(ind (-> inds (cl-first) (cdr))))
(when (> (length inds) 1)
(message "Winum conflict - window %s was assigned an index by multiple custom assign functions: '%s'"
window (--map (format "%s -> %S" (car it) (cdr it)) inds)))
(winum--assign window ind)
(push ind winum--assigned-indices)))))
(defun winum--assign (window &optional number)
"Assign to window WINDOW the number NUMBER.
If NUMBER is not specified, determine it first based on `winum--remaining'.
Returns the assigned number, or nil on error."
(if number
(defun winum--assign (window index)
"Assign to window WINDOW the index INDEX.
Returns the assigned index, or nil on error."
(if (gethash index (winum--get-window-table))
(progn
(winum--maybe-expand-window-vector number)
(if (aref (winum--get-window-vector) number)
(progn
(message "Number %s already assigned to %s, can't assign to %s"
number (aref (winum--get-window-vector) number) window)
nil)
(setf (aref (winum--get-window-vector) number) window)
(puthash window number (winum--get-numbers-table))
(setq winum--remaining (delq number winum--remaining))
number))
;; else determine number and assign
(when winum--remaining
(unless (gethash window (winum--get-numbers-table))
(let ((number (car winum--remaining)))
(winum--assign window number))))))
(defun winum--maybe-expand-window-vector (number)
"Expand `winum--window-vector' if NUMBER is bigger than its size.
The size of `winum--window-vector' is normally based on the number of live
windows, however a higher number can be reserved by the user-defined
`winum-assign-func'."
(let* ((window-vector (winum--get-window-vector))
(window-vector-length (length window-vector)))
(when (> number window-vector-length)
(winum--set-window-vector
(vconcat window-vector
(make-vector (1+ (- number window-vector-length)) nil))))))
(message "Index %S already assigned to %s, can't assign to %s"
index (gethash index (winum--get-window-table)) window)
nil)
(puthash index window (winum--get-window-table))
(puthash window index (winum--get-index-table))
index))
(defun winum--window-list ()
"Return a list of interesting windows."
@ -537,10 +564,10 @@ windows, however a higher number can be reserved by the user-defined
(frame-local
(winum--list-windows-in-frame))
(t
(error "Invalid `winum-scope': %S" winum-scope)))))
(user-error "Invalid `winum-scope': %S" winum-scope)))))
(defun winum--ignore-window-p (window)
"Non-nil if WINDOW should be ignored for numbering."
"Non-nil if WINDOW should be ignored for indexing."
(let ((f (window-frame window)))
(or (not (and (frame-live-p f)
(frame-visible-p f)))
@ -554,33 +581,25 @@ windows, however a higher number can be reserved by the user-defined
"List windows in frame F using natural Emacs ordering."
(window-list f 0 (frame-first-window f)))
(defun winum--set-window-vector (window-vector)
"Set WINDOW-VECTOR according to the current `winum-scope'."
(winum--check-for-scope-change)
(if (eq winum-scope 'frame-local)
(puthash (selected-frame)
(cons window-vector
(make-hash-table :size winum--window-count))
winum--frames-table)
(setq winum--window-vector window-vector)))
(defun winum--get-window-vector ()
"Return the window vector used to get a window given a number.
This vector is not stored the same way depending on the value of `winum-scope'."
(defun winum--get-window-table ()
"Return the window table used to get a window given an index.
This hashtable is not stored the same way depending on the value of
`winum-scope'."
(winum--check-for-scope-change)
(winum--check-frames-table)
(if (eq winum-scope 'frame-local)
(car (gethash (selected-frame) winum--frames-table))
winum--window-vector))
winum--window-table))
(defun winum--get-numbers-table ()
"Return the numbers hashtable used to get a number given a window.
(defun winum--get-index-table ()
"Return the index hashtable used to get an index given a window.
This hashtable is not stored the same way depending on the value of
`winum-scope'"
`winum-scope'."
(winum--check-for-scope-change)
(winum--check-frames-table)
(if (eq winum-scope 'frame-local)
(cdr (gethash (selected-frame) winum--frames-table))
winum--numbers-table))
winum--index-table))
(defun winum--check-frames-table ()
"Make sure `winum--frames-table' exists and is correctly equipped.
@ -593,17 +612,12 @@ Verifies 2 things (when `winum-scope' is frame local):
(unless winum--frames-table
(setq winum--frames-table (make-hash-table :size winum--max-frames)))
(unless (gethash (selected-frame) winum--frames-table)
(puthash (selected-frame) (cons
(make-hash-table :test 'equal)
(make-hash-table :test 'equal))
winum--frames-table)
(winum--update))))
(defun winum--available-numbers ()
"Return a list of numbers from 1 to `winum--window-count'.
0 is is not part of the list as its assignment is either manual
using the `winum-assign-func', or using `winum-auto-assign-0-to-minibuffer'."
(let ((numbers))
(dotimes (i winum--window-count)
(push (1+ i) numbers))
(nreverse numbers)))
(defun winum--switch-to-window (window)
"Switch to the window WINDOW and switch input focus if on a different frame."
(let ((frame (window-frame window)))
@ -629,10 +643,6 @@ internal data structures according to the new scope."
(add-hook 'delete-frame-functions #'winum--remove-deleted-frame-from-frames-table)
(push "^No window numbered .$" debug-ignored-errors)
(push "^Got a dead window .$" debug-ignored-errors)
(push "^Invalid `winum-scope': .$" debug-ignored-errors)
(provide 'winum)
;;; winum.el ends here