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."
This commit is contained in:
Kiana Sheibani 2024-08-26 19:04:33 -04:00
parent c5455e866e
commit e0c329e0f6
Signed by: toki
GPG key ID: 6CB106C25E86A9F7
2 changed files with 215 additions and 204 deletions

View file

@ -19,14 +19,13 @@
- [[#future-developments][Future developments]] - [[#future-developments][Future developments]]
* Introduction * Introduction
Window numbers for Emacs: Navigate your windows and frames using numbers !
This package is an extended and actively maintained version of the 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 by Nikolaj Schumacher, with some ideas and code taken [[https://github.com/nschum/window-numbering.el][window-numbering]] package.
from [[https://github.com/abo-abo/ace-window][ace-window]].
This version brings, among other things, support for number sets across multiple Compared to winum, this fork adds support for windows having arbitrary Lisp values
frames, giving the user a smoother experience of multi-screen Emacs. as identifiers, as opposed to strictly increasing numbers. The public
API is largely the same, however, and simple numbers are still the default.
* Changelog * Changelog
** Sep. 11 2019 ** Sep. 11 2019

392
winum.el
View file

@ -65,18 +65,15 @@ Has effect only when `winum-scope' is not 'frame-local."
:group 'winum :group 'winum
:type 'boolean) :type 'boolean)
(defcustom winum-auto-assign-0-to-minibuffer t (defcustom winum-minibuffer-auto-assign 0
"If non-nil, `winum-mode' assigns 0 to the minibuffer when active." "If non-nil, `winum-mode' automatically assigns this index to the minibuffer."
:group 'winum :group 'winum
:type 'boolean) :type 'sexp)
(defcustom winum-assign-func nil (defcustom winum-assign-func nil
"Function called for each window by `winum-mode'. "Function called for each window by `winum-mode'.
This is called before automatic assignment begins. The function should This is called before automatic assignment begins. The function should
return a number to have it assigned to the current-window, nil otherwise. return an index 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.
Example: always assign *Calculator* the number 9 and *NeoTree* the number 0: Example: always assign *Calculator* the number 9 and *NeoTree* the number 0:
@ -98,21 +95,18 @@ Example: always assign *Calculator* the number 9 and *NeoTree* the number 0:
(defcustom winum-assign-functions nil (defcustom winum-assign-functions nil
"List of functions called for each window by `winum-mode'. "List of functions called for each window by `winum-mode'.
These functions allow for deterministic assignment of numbers to windows. Each These functions allow for deterministic assignment of indices to windows. Each
function is called for every window. A function should return the number to be 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 number for assigned to a window or nil. The *first* function to output a value for a
a given window will determine this window's number. given window will determine this window's number.
If the list is empty or if every functions returns nil for a given window winum 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* 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 packages at once it should never be directly set, only added to and removed
from. 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 Example: always assign *Calculator* the number 9, *Flycheck-errors* the number 8
and *NeoTree* the number 0: and *NeoTree* the number 0:
@ -129,7 +123,21 @@ and *NeoTree* the number 0:
(add-to-list (add-to-list
'winum-assign-functions #'winum-assign-0-to-neotree)" 'winum-assign-functions #'winum-assign-0-to-neotree)"
:group 'winum :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 (defcustom winum-auto-setup-mode-line t
"When nil, `winum-mode' will not display window numbers in the mode-line. "When nil, `winum-mode' will not display window numbers in the mode-line.
@ -143,33 +151,38 @@ numbers in the mode-line."
:group 'winum :group 'winum
:type 'integer) :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 " (defcustom winum-format " %s "
"Format string defining how the window number looks like in the mode-line. "Format string defining how the window number looks like in the mode-line.
This string is passed to the `format' function along with the This string is passed to the `format' function along with the index."
result of `winum-get-number-string'."
:group 'winum :group 'winum
:type 'string) :type 'string)
(defcustom winum-ignored-buffers '(" *which-key*") (defcustom winum-ignored-buffers '(" *which-key*")
"List of buffers to ignore when assigning numbers." "List of buffers to ignore when assigning indices."
:group 'winum :group 'winum
:type '(repeat string)) :type '(repeat string))
(defcustom winum-ignored-buffers-regexp '() (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'" See Info node `(emacs) Regexps' or Info node `(elisp) Regular Expressions'"
:group 'winum :group 'winum
:type '(repeat string) :type '(repeat string)
:risky t) :risky t)
(defface winum-face '() (defface winum-face '()
"Face used for the number in the mode-line." "Face used for the index in the mode-line."
:group 'winum) :group 'winum)
(defvar winum-base-map (defvar winum-base-map
(let ((map (make-sparse-keymap))) (let ((map (make-sparse-keymap)))
(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-number) (define-key map (kbd "²") 'winum-select-window-by-index)
(define-key map (kbd "0") 'winum-select-window-0-or-10) (define-key map (kbd "0") 'winum-select-window-0-or-10)
(define-key map (kbd "1") 'winum-select-window-1) (define-key map (kbd "1") 'winum-select-window-1)
(define-key map (kbd "2") 'winum-select-window-2) (define-key map (kbd "2") 'winum-select-window-2)
@ -183,7 +196,7 @@ See Info node `(emacs) Regexps' or Info node `(elisp) Regular Expressions'"
map) map)
"Keymap to be used under the prefix provided by `winum-keymap-prefix'.") "Keymap to be used under the prefix provided by `winum-keymap-prefix'.")
(defvar winum-keymap (let ((map (make-sparse-keymap))) (defvar winum-mode-map (let ((map (make-sparse-keymap)))
(define-key map (kbd "C-x w") winum-base-map) (define-key map (kbd "C-x w") winum-base-map)
map) map)
"Keymap used for `winum-mode'.") "Keymap used for `winum-mode'.")
@ -193,37 +206,36 @@ See Info node `(emacs) Regexps' or Info node `(elisp) Regular Expressions'"
(defvar winum--max-frames 16 (defvar winum--max-frames 16
"Maximum number of frames that can be numbered.") "Maximum number of frames that can be numbered.")
(defvar winum--window-count nil (defvar winum--assigned-indices nil
"Current count of windows to be numbered.") "List of indices that have already been assigned by `winum-assign-functions'.")
(defvar winum--remaining nil (defvar winum--window-table nil
"A list of window numbers to assign.") "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 (defvar winum--index-table nil
"Vector of windows indexed by their number. "Hash table with windows as keys and indices as values.
Used internally by winum to get a window provided a number.") Used internally by winum to get an index provided a window.")
(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--frames-table nil (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 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 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. as values.
To get a window given a number, use the `car' of a value. To get a window given an index, use the `car' of a value.
To get a number given a window, use the `cdr' 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.") Such a structure allows for per-frame bidirectional fast access.")
(defvar winum--mode-line-segment (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.") "What is pushed into `mode-line-format' when setting it up automatically.")
(defvar winum--last-used-scope winum-scope (defvar winum--last-used-scope winum-scope
@ -235,9 +247,7 @@ Needed to detect scope changes at runtime.")
;;;###autoload ;;;###autoload
(define-minor-mode winum-mode (define-minor-mode winum-mode
"A minor mode that allows for managing windows based on window numbers." "A minor mode that allows for managing windows based on window numbers."
nil :lighter nil
nil
winum-keymap
:global t :global t
(if winum-mode (if winum-mode
(winum--init) (winum--init)
@ -245,115 +255,145 @@ Needed to detect scope changes at runtime.")
;;;###autoload ;;;###autoload
(defun winum-select-window-0-or-10 (&optional arg) (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." If prefix ARG is given, delete the window instead of selecting it."
(interactive "P") (interactive "P")
(let ((n (if (winum-get-window-by-number 0) (let ((n (if (winum-get-window-by-index 0) 0 10)))
(if arg '- 0) (if arg
(if arg -10 10)))) (winum-delete-window-by-index n)
(winum-select-window-by-number n))) (winum-select-window-by-index n))))
;;;###autoload ;;;###autoload
(defun winum-select-window-0 (&optional arg) (defun winum-select-window-0 (&optional arg)
"Jump to window 0. "Jump to window 0.
If prefix ARG is given, delete the window instead of selecting it." If prefix ARG is given, delete the window instead of selecting it."
(interactive "P") (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 ;;;###autoload
(defun winum-select-window-1 (&optional arg) (defun winum-select-window-1 (&optional arg)
"Jump to window 1. "Jump to window 1.
If prefix ARG is given, delete the window instead of selecting it." If prefix ARG is given, delete the window instead of selecting it."
(interactive "P") (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 ;;;###autoload
(defun winum-select-window-2 (&optional arg) (defun winum-select-window-2 (&optional arg)
"Jump to window 2. "Jump to window 2.
If prefix ARG is given, delete the window instead of selecting it." If prefix ARG is given, delete the window instead of selecting it."
(interactive "P") (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 ;;;###autoload
(defun winum-select-window-3 (&optional arg) (defun winum-select-window-3 (&optional arg)
"Jump to window 3. "Jump to window 3.
If prefix ARG is given, delete the window instead of selecting it." If prefix ARG is given, delete the window instead of selecting it."
(interactive "P") (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 ;;;###autoload
(defun winum-select-window-4 (&optional arg) (defun winum-select-window-4 (&optional arg)
"Jump to window 4. "Jump to window 4.
If prefix ARG is given, delete the window instead of selecting it." If prefix ARG is given, delete the window instead of selecting it."
(interactive "P") (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 ;;;###autoload
(defun winum-select-window-5 (&optional arg) (defun winum-select-window-5 (&optional arg)
"Jump to window 5. "Jump to window 5.
If prefix ARG is given, delete the window instead of selecting it." If prefix ARG is given, delete the window instead of selecting it."
(interactive "P") (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 ;;;###autoload
(defun winum-select-window-6 (&optional arg) (defun winum-select-window-6 (&optional arg)
"Jump to window 6. "Jump to window 6.
If prefix ARG is given, delete the window instead of selecting it." If prefix ARG is given, delete the window instead of selecting it."
(interactive "P") (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 ;;;###autoload
(defun winum-select-window-7 (&optional arg) (defun winum-select-window-7 (&optional arg)
"Jump to window 7. "Jump to window 7.
If prefix ARG is given, delete the window instead of selecting it." If prefix ARG is given, delete the window instead of selecting it."
(interactive "P") (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 ;;;###autoload
(defun winum-select-window-8 (&optional arg) (defun winum-select-window-8 (&optional arg)
"Jump to window 8. "Jump to window 8.
If prefix ARG is given, delete the window instead of selecting it." If prefix ARG is given, delete the window instead of selecting it."
(interactive "P") (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 ;;;###autoload
(defun winum-select-window-9 (&optional arg) (defun winum-select-window-9 (&optional arg)
"Jump to window 9. "Jump to window 9.
If prefix ARG is given, delete the window instead of selecting it." If prefix ARG is given, delete the window instead of selecting it."
(interactive "P") (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 ;;;###autoload
(defun winum-select-window-by-number (&optional arg) (defun winum-select-window-by-index (&optional arg)
"Select or delete window which number is specified by ARG. "Select window whose index is specified by ARG.
If the number is negative, delete the window instead of selecting it. There are several ways to provide the value:
There are several ways to provide the number:
- if called from elisp with an argument, use it. - 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 a numeric prefix argument, use it.
- if prefix argument is the negative argument, delete window 0. - if called interactively with the default prefix argument, use current window.
- if prefix argument is the default prefix argument, delete current window. - if called interactively and no argument is provided, read from minibuffer."
- if called interactively and no valid argument is provided, read from
minibuffer."
(interactive "P") (interactive "P")
(let* ((n (cond (let* ((i (if (called-interactively-p 'any)
(cond
((integerp arg) arg) ((integerp arg) arg)
((eq arg '-) 0) ; the negative argument (arg (winum-get-index))
(arg (winum-get-number)) (t (read--expression "Window: ")))
((called-interactively-p 'any) (or arg (winum-get-index))))
(let ((user-input-str (read-from-minibuffer "Window: "))) (w (winum-get-window-by-index i)))
(if (not (string-match-p "[+-]?[0-9]+\.*" user-input-str)) (if w
(winum-get-number) (winum--switch-to-window w)
(string-to-number user-input-str)))) (user-error "No window with index %S" i))))
(t (winum-get-number))))
(w (winum-get-window-by-number (abs n))) ;;;###autoload
(delete (and arg (defun winum-delete-window-by-index (&optional arg)
(or (not (integerp arg)) "Delete window whose index is specified by ARG.
(> 0 n))))) 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 (if w
(if delete
(delete-window w) (delete-window w)
(winum--switch-to-window w)) (user-error "No window with index %S" i))))
(error "No window numbered %d" n))))
;; Public API ------------------------------------------------------------------ ;; Public API ------------------------------------------------------------------
@ -371,41 +411,38 @@ PREFIX must be a key sequence, like the ones returned by `kbd'."
winum-keymap)) winum-keymap))
;;;###autoload ;;;###autoload
(defun winum-get-window-by-number (n) (defun winum-get-window-by-index (index)
"Return window numbered N if exists, nil otherwise." "Return window INDEX if exists, nil otherwise."
(let ((window-vector (winum--get-window-vector))) (let ((window-table (winum--get-window-table)))
(when (and (>= n 0) (< n (length window-vector))) (gethash index window-table)))
(aref window-vector n))))
;;;###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 ;;;###autoload
(defun winum-get-number-string (&optional window) (defun winum-get-number-string (&optional window)
"Get the current or specified window's current number as a propertized string. "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. WINDOW: if specified, the window of which we want to know the number.
If not specified, the number of the currently selected window is If not specified, the number of the currently selected window is
returned." returned."
(let* ((n (winum-get-number window)) (let* ((n (winum-get-index window))
(s (if (numberp n) (s (if (funcall winum-mode-line-p n)
(int-to-string n) (format "%s" n)
""))) "")))
(propertize s 'face 'winum-face))) (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 ---------------------------------------------------------- ;; Internal functions ----------------------------------------------------------
(defun winum--init () (defun winum--init ()
"Initialize winum-mode." "Initialize winum-mode."
(setq winum--window-count (length (winum--window-list)))
(if (eq winum-scope 'frame-local) (if (eq winum-scope 'frame-local)
(setq winum--frames-table (make-hash-table :size winum--max-frames)) (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 (when winum-auto-setup-mode-line
(winum--install-mode-line)) (winum--install-mode-line))
(add-hook 'minibuffer-setup-hook 'winum--update) (add-hook 'minibuffer-setup-hook 'winum--update)
@ -423,7 +460,7 @@ WINDOW: if specified, the window of which we want to know the number.
(setq winum--frames-table nil)) (setq winum--frames-table nil))
(defun winum--install-mode-line (&optional position) (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." POSITION: position in the mode-line."
(let ((mode-line (default-value 'mode-line-format)) (let ((mode-line (default-value 'mode-line-format))
res) res)
@ -440,7 +477,7 @@ POSITION: position in the mode-line."
(force-mode-line-update t)) (force-mode-line-update t))
(defun winum--clear-mode-line () (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)) (let ((mode-line (default-value 'mode-line-format))
res) res)
(while mode-line (while mode-line
@ -453,71 +490,63 @@ POSITION: position in the mode-line."
(force-mode-line-update t)) (force-mode-line-update t))
(defun winum--update () (defun winum--update ()
"Update window numbers." "Update window indices."
(let ((windows (winum--window-list))) (let ((windows (winum--window-list)))
(setq winum--window-count (length windows) (setq winum--assigned-indices nil)
winum--remaining (winum--available-numbers)) (clrhash (winum--get-window-table))
(winum--set-window-vector (make-vector (1+ winum--window-count) nil)) (clrhash (winum--get-index-table))
(clrhash (winum--get-numbers-table))
(when winum-assign-functions (when winum-assign-functions
(-each windows #'winum--try-to-find-custom-number)) (--each (-copy windows) (when (winum--try-to-find-custom-index it)
(when (and winum-auto-assign-0-to-minibuffer (setq windows (delq it windows)))))
(when (and winum-minibuffer-auto-assign
(active-minibuffer-window) (active-minibuffer-window)
(not (winum-get-window-by-number 0))) (not (winum-get-window-by-index winum-minibuffer-auto-assign)))
(winum--assign (active-minibuffer-window) 0)) (winum--assign (active-minibuffer-window) winum-minibuffer-auto-assign)
(dolist (w windows) (push winum-minibuffer-auto-assign winum--assigned-indices))
(winum--assign w)))) ;; Auto-assign remaining windows
(when windows
(funcall winum-auto-assign-function windows))))
(defun winum--try-to-find-custom-number (window) (defun winum--auto-assign (windows)
"Try to find and assign a custom number for WINDOW. "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 Do so by trying every function in `winum-assign-functions' and assign the
*first* non nil integer. *first* non nil value.
When multiple functions assign a number to a window log a warning and use the When multiple functions assign an index to a window log a warning and use the
first number anyway." first index anyway."
(with-selected-window window (with-selected-window window
(with-current-buffer (window-buffer window) (with-current-buffer (window-buffer window)
(let* ((nums (->> winum-assign-functions (when-let* ((inds (->> winum-assign-functions
(--map (cons it (funcall it))) (--map (cons it (funcall it)))
(--remove (null (cdr it))))) (--remove (null (cdr it)))))
(num (-> nums (cl-first) (cdr)))) (ind (-> inds (cl-first) (cdr))))
(when (> (length nums) 1) (when (> (length inds) 1)
(message "Winum conflict - window %s was assigned a number by multiple custom assign functions: '%s'" (message "Winum conflict - window %s was assigned an index by multiple custom assign functions: '%s'"
window (--map (format "%s -> %s" (car it) (cdr it)) nums))) window (--map (format "%s -> %S" (car it) (cdr it)) inds)))
(when (integerp num) (winum--assign window num)))))) (winum--assign window ind)
(push ind winum--assigned-indices)))))
(defun winum--assign (window &optional number) (defun winum--assign (window index)
"Assign to window WINDOW the number NUMBER. "Assign to window WINDOW the index INDEX.
If NUMBER is not specified, determine it first based on `winum--remaining'. Returns the assigned index, or nil on error."
Returns the assigned number, or nil on error." (if (gethash index (winum--get-window-table))
(if number
(progn (progn
(winum--maybe-expand-window-vector number) (message "Index %S already assigned to %s, can't assign to %s"
(if (aref (winum--get-window-vector) number) index (gethash index (winum--get-window-table)) window)
(progn
(message "Number %s already assigned to %s, can't assign to %s"
number (aref (winum--get-window-vector) number) window)
nil) nil)
(setf (aref (winum--get-window-vector) number) window) (puthash index window (winum--get-window-table))
(puthash window number (winum--get-numbers-table)) (puthash window index (winum--get-index-table))
(setq winum--remaining (delq number winum--remaining)) index))
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))))))
(defun winum--window-list () (defun winum--window-list ()
"Return a list of interesting windows." "Return a list of interesting windows."
@ -537,10 +566,10 @@ windows, however a higher number can be reserved by the user-defined
(frame-local (frame-local
(winum--list-windows-in-frame)) (winum--list-windows-in-frame))
(t (t
(error "Invalid `winum-scope': %S" winum-scope))))) (user-error "Invalid `winum-scope': %S" winum-scope)))))
(defun winum--ignore-window-p (window) (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))) (let ((f (window-frame window)))
(or (not (and (frame-live-p f) (or (not (and (frame-live-p f)
(frame-visible-p f))) (frame-visible-p f)))
@ -554,33 +583,25 @@ windows, however a higher number can be reserved by the user-defined
"List windows in frame F using natural Emacs ordering." "List windows in frame F using natural Emacs ordering."
(window-list f 0 (frame-first-window f))) (window-list f 0 (frame-first-window f)))
(defun winum--set-window-vector (window-vector) (defun winum--get-window-table ()
"Set WINDOW-VECTOR according to the current `winum-scope'." "Return the window table used to get a window given an index.
(winum--check-for-scope-change) This hashtable is not stored the same way depending on the value of
(if (eq winum-scope 'frame-local) `winum-scope'."
(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'."
(winum--check-for-scope-change) (winum--check-for-scope-change)
(winum--check-frames-table)
(if (eq winum-scope 'frame-local) (if (eq winum-scope 'frame-local)
(car (gethash (selected-frame) winum--frames-table)) (car (gethash (selected-frame) winum--frames-table))
winum--window-vector)) winum--window-table))
(defun winum--get-numbers-table () (defun winum--get-index-table ()
"Return the numbers hashtable used to get a number given a window. "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 This hashtable is not stored the same way depending on the value of
`winum-scope'" `winum-scope'."
(winum--check-for-scope-change) (winum--check-for-scope-change)
(winum--check-frames-table) (winum--check-frames-table)
(if (eq winum-scope 'frame-local) (if (eq winum-scope 'frame-local)
(cdr (gethash (selected-frame) winum--frames-table)) (cdr (gethash (selected-frame) winum--frames-table))
winum--numbers-table)) winum--index-table))
(defun winum--check-frames-table () (defun winum--check-frames-table ()
"Make sure `winum--frames-table' exists and is correctly equipped. "Make sure `winum--frames-table' exists and is correctly equipped.
@ -593,17 +614,12 @@ Verifies 2 things (when `winum-scope' is frame local):
(unless winum--frames-table (unless winum--frames-table
(setq winum--frames-table (make-hash-table :size winum--max-frames))) (setq winum--frames-table (make-hash-table :size winum--max-frames)))
(unless (gethash (selected-frame) winum--frames-table) (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)))) (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) (defun winum--switch-to-window (window)
"Switch to the window WINDOW and switch input focus if on a different frame." "Switch to the window WINDOW and switch input focus if on a different frame."
(let ((frame (window-frame window))) (let ((frame (window-frame window)))
@ -629,10 +645,6 @@ internal data structures according to the new scope."
(add-hook 'delete-frame-functions #'winum--remove-deleted-frame-from-frames-table) (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) (provide 'winum)
;;; winum.el ends here ;;; winum.el ends here