Compare commits

..

No commits in common. "aca0f74f3a442b1b1a6f4315303c340f7c1005b8" and "c5455e866e8a5f7eab6a7263e2057aff5f1118b9" have entirely different histories.

2 changed files with 228 additions and 249 deletions

View file

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

416
winum.el
View file

@ -1,8 +1,7 @@
;;; winum.el --- Navigate windows and frames using numbers. ;;; winum.el --- Navigate windows and frames using numbers.
;; ;;
;; Copyright (c) 2006-2015 Nikolaj Schumacher ;; Copyright (c) 2006-2015 Nikolaj Schumacher
;; Copyright (c) 2016-2019 Thomas Chauvot de Beauchêne ;; Copyright (c) 2016 Thomas Chauvot de Beauchêne
;; Copyright (c) 2024 Kiana Sheibani
;; ;;
;; This program is free software: you can redistribute it and/or modify ;; 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 ;; it under the terms of the GNU General Public License as published by
@ -17,8 +16,8 @@
;; You should have received a copy of the GNU General Public License ;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; ;;
;; Author: Kiana Sheibani <kiana.a.sheibani@gmail.com> ;; Author: Thomas de Beauchêne <thomas.de.beauchene@gmail.com>
;; Version: 3.0.0 ;; Version: 2.2.0
;; Keywords: convenience, frames, windows, multi-screen ;; Keywords: convenience, frames, windows, multi-screen
;; URL: http://github.com/deb0ch/winum.el ;; URL: http://github.com/deb0ch/winum.el
;; Created: 2016 ;; Created: 2016
@ -39,6 +38,9 @@
;; frames, giving the user a smoother experience of multi-screen Emacs. ;; frames, giving the user a smoother experience of multi-screen Emacs.
;; ;;
;;; Code: ;;; Code:
;;
;; FIXME: The mode-line's window number is not always up to date in all frames.
;;
(eval-when-compile (require 'cl-lib)) (eval-when-compile (require 'cl-lib))
(require 'dash) (require 'dash)
@ -63,15 +65,18 @@ Has effect only when `winum-scope' is not 'frame-local."
:group 'winum :group 'winum
:type 'boolean) :type 'boolean)
(defcustom winum-minibuffer-auto-assign 0 (defcustom winum-auto-assign-0-to-minibuffer t
"If non-nil, `winum-mode' automatically assigns this index to the minibuffer." "If non-nil, `winum-mode' assigns 0 to the minibuffer when active."
:group 'winum :group 'winum
:type 'sexp) :type 'boolean)
(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 an index to have it assigned to the current-window, nil otherwise. 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.
Example: always assign *Calculator* the number 9 and *NeoTree* the number 0: Example: always assign *Calculator* the number 9 and *NeoTree* the number 0:
@ -93,18 +98,21 @@ 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 indices to windows. Each These functions allow for deterministic assignment of numbers to windows. Each
function is called for every window. A function should return the index to be 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 value for a assigned to a window or nil. The *first* function to output a number for
given window will determine this window's number. 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 If the list is empty or if every functions returns nil for a given window winum
will proceed to automatic assignment. will proceed to automatic number 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:
@ -121,21 +129,7 @@ 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 '(repeat function)) :type 'list)
(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.
@ -149,38 +143,33 @@ 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 index." This string is passed to the `format' function along with the
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 indices." "List of buffers to ignore when assigning numbers."
: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 indices. "List of regexps for buffer names to ignore when assigning numbers.
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 index in the mode-line." "Face used for the number 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-index) (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 "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)
@ -194,9 +183,9 @@ 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-mode-map (let ((map (make-sparse-keymap))) (defvar winum-keymap (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'.")
;; Internal variables ---------------------------------------------------------- ;; Internal variables ----------------------------------------------------------
@ -204,36 +193,37 @@ 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--assigned-indices nil (defvar winum--window-count nil
"List of indices that have already been assigned by `winum-assign-functions'.") "Current count of windows to be numbered.")
(defvar winum--window-table nil (defvar winum--remaining nil
"Hash table with indices as keys and windows as values. "A list of window numbers to assign.")
Used internally by winum to get a window provided an index.")
(defvar winum--index-table nil (defvar winum--window-vector nil
"Hash table with windows as keys and indices as values. "Vector of windows indexed by their number.
Used internally by winum to get an index provided a window.") 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--frames-table nil (defvar winum--frames-table nil
"Table linking windows to indices and indices to windows for each frame. "Table linking windows to numbers and numbers 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 index sets in every frame. separate window numbers 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-table' . `winum--index-table') \(`winum--window-vector' . `winum--numbers-table')
as values. as values.
To get a window given an index, use the `car' of a value. To get a window given a number, use the `car' of a value.
To get an index given a window, use the `cdr' of a value. To get a number 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 (let ((index (winum-get-index))) '(:eval (format winum-format (winum-get-number-string)))
(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
@ -245,7 +235,9 @@ 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."
:lighter nil nil
nil
winum-keymap
:global t :global t
(if winum-mode (if winum-mode
(winum--init) (winum--init)
@ -253,145 +245,115 @@ 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, or window 10 if 0 is not assigned. "Jump to window 0 if assigned or 10 if exists.
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-index 0) 0 10))) (let ((n (if (winum-get-window-by-number 0)
(if arg (if arg '- 0)
(winum-delete-window-by-index n) (if arg -10 10))))
(winum-select-window-by-index n)))) (winum-select-window-by-number 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")
(if arg (winum-select-window-by-number (if arg '- 0)))
(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")
(if arg (winum-select-window-by-number (if arg -1 1)))
(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")
(if arg (winum-select-window-by-number (if arg -2 2)))
(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")
(if arg (winum-select-window-by-number (if arg -3 3)))
(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")
(if arg (winum-select-window-by-number (if arg -4 4)))
(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")
(if arg (winum-select-window-by-number (if arg -5 5)))
(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")
(if arg (winum-select-window-by-number (if arg -6 6)))
(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")
(if arg (winum-select-window-by-number (if arg -7 7)))
(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")
(if arg (winum-select-window-by-number (if arg -8 8)))
(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")
(if arg (winum-select-window-by-number (if arg -9 9)))
(winum-delete-window-by-index 9)
(winum-select-window-by-index 9)))
;;;###autoload ;;;###autoload
(defun winum-select-window-by-index (&optional arg) (defun winum-select-window-by-number (&optional arg)
"Select window whose index is specified by ARG. "Select or delete window which number is specified by ARG.
There are several ways to provide the value: If the number is negative, delete the window instead of selecting it.
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 called interactively with the default prefix argument, use current window. - if prefix argument is the negative argument, delete window 0.
- if called interactively and no argument is provided, read from minibuffer." - if prefix argument is the default prefix argument, delete current window.
- if called interactively and no valid argument is provided, read from
minibuffer."
(interactive "P") (interactive "P")
(let* ((i (if (called-interactively-p 'any) (let* ((n (cond
(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))
(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)))))
(if w (if w
(winum--switch-to-window w) (if delete
(user-error "No window with index %S" i)))) (delete-window w)
(winum--switch-to-window w))
;;;###autoload (error "No window numbered %d" n))))
(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 ------------------------------------------------------------------ ;; Public API ------------------------------------------------------------------
@ -409,38 +371,41 @@ PREFIX must be a key sequence, like the ones returned by `kbd'."
winum-keymap)) winum-keymap))
;;;###autoload ;;;###autoload
(defun winum-get-window-by-index (index) (defun winum-get-window-by-number (n)
"Return window INDEX if exists, nil otherwise." "Return window numbered N if exists, nil otherwise."
(let ((window-table (winum--get-window-table))) (let ((window-vector (winum--get-window-vector)))
(gethash index window-table))) (when (and (>= n 0) (< n (length window-vector)))
(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-index window)) (let* ((n (winum-get-number window))
(s (if (funcall winum-mode-line-p n) (s (if (numberp n)
(format "%s" n) (int-to-string 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--window-table (make-hash-table :test 'equal) (setq winum--numbers-table (make-hash-table :size winum--window-count)))
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)
@ -458,7 +423,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 index from `winum-mode' to the mode-line. "Install the window number 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)
@ -475,76 +440,84 @@ 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 index of `winum-mode' from the mode-line." "Remove the window number 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
(let ((item (pop mode-line))) (let ((item (pop mode-line)))
(unless (equal item winum--mode-line-segment) (unless (equal item winum--mode-line-segment)
(push item res)))) (push item res))))
(let ((nres (nreverse res))) (let ((nres (nreverse res)))
(setq mode-line-format nres) (setq mode-line-format nres)
(setq-default mode-line-format nres))) (setq-default mode-line-format nres)))
(force-mode-line-update t)) (force-mode-line-update t))
(defun winum--update () (defun winum--update ()
"Update window indices." "Update window numbers."
(let ((windows (winum--window-list))) (let ((windows (winum--window-list)))
(setq winum--assigned-indices nil) (setq winum--window-count (length windows)
(clrhash (winum--get-window-table)) winum--remaining (winum--available-numbers))
(clrhash (winum--get-index-table)) (winum--set-window-vector (make-vector (1+ winum--window-count) nil))
(clrhash (winum--get-numbers-table))
(when winum-assign-functions (when winum-assign-functions
(--each (-copy windows) (when (winum--try-to-find-custom-index it) (-each windows #'winum--try-to-find-custom-number))
(setq windows (delq it windows))))) (when (and winum-auto-assign-0-to-minibuffer
(when (and winum-minibuffer-auto-assign
(active-minibuffer-window) (active-minibuffer-window)
(not (winum-get-window-by-index winum-minibuffer-auto-assign))) (not (winum-get-window-by-number 0)))
(winum--assign (active-minibuffer-window) winum-minibuffer-auto-assign) (winum--assign (active-minibuffer-window) 0))
(push winum-minibuffer-auto-assign winum--assigned-indices))
;; Auto-assign remaining windows
(when windows
(funcall winum-auto-assign-function windows))))
(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) (dolist (w windows)
(while (member index winum--assigned-indices) (winum--assign w))))
(setq index (1+ index)))
(winum--assign w index)
(setq index (1+ index)))))
(defun winum--try-to-find-custom-index (window) (defun winum--try-to-find-custom-number (window)
"Try to find and assign a custom index for WINDOW. "Try to find and assign a custom number 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 value. *first* non nil integer.
When multiple functions assign an index to a window log a warning and use the When multiple functions assign a number to a window log a warning and use the
first index anyway." first number anyway."
(with-selected-window window (with-selected-window window
(with-current-buffer (window-buffer window) (with-current-buffer (window-buffer window)
(when-let* ((inds (->> winum-assign-functions (let* ((nums (->> winum-assign-functions
(--map (cons it (funcall it))) (--map (cons it (funcall it)))
(--remove (null (cdr it))))) (--remove (null (cdr it)))))
(ind (-> inds (cl-first) (cdr)))) (num (-> nums (cl-first) (cdr))))
(when (> (length inds) 1) (when (> (length nums) 1)
(message "Winum conflict - window %s was assigned an index by multiple custom assign functions: '%s'" (message "Winum conflict - window %s was assigned a number by multiple custom assign functions: '%s'"
window (--map (format "%s -> %S" (car it) (cdr it)) inds))) window (--map (format "%s -> %s" (car it) (cdr it)) nums)))
(winum--assign window ind) (when (integerp num) (winum--assign window num))))))
(push ind winum--assigned-indices)))))
(defun winum--assign (window index) (defun winum--assign (window &optional number)
"Assign to window WINDOW the index INDEX. "Assign to window WINDOW the number NUMBER.
Returns the assigned index, or nil on error." If NUMBER is not specified, determine it first based on `winum--remaining'.
(if (gethash index (winum--get-window-table)) Returns the assigned number, or nil on error."
(if number
(progn (progn
(message "Index %S already assigned to %s, can't assign to %s" (winum--maybe-expand-window-vector number)
index (gethash index (winum--get-window-table)) window) (if (aref (winum--get-window-vector) number)
nil) (progn
(puthash index window (winum--get-window-table)) (message "Number %s already assigned to %s, can't assign to %s"
(puthash window index (winum--get-index-table)) number (aref (winum--get-window-vector) number) window)
index)) 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))))))
(defun winum--window-list () (defun winum--window-list ()
"Return a list of interesting windows." "Return a list of interesting windows."
@ -564,10 +537,10 @@ Returns the assigned index, or nil on error."
(frame-local (frame-local
(winum--list-windows-in-frame)) (winum--list-windows-in-frame))
(t (t
(user-error "Invalid `winum-scope': %S" winum-scope))))) (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 indexing." "Non-nil if WINDOW should be ignored for numbering."
(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)))
@ -581,25 +554,33 @@ Returns the assigned index, or nil on error."
"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--get-window-table () (defun winum--set-window-vector (window-vector)
"Return the window table used to get a window given an index. "Set WINDOW-VECTOR according to the current `winum-scope'."
This hashtable is not stored the same way depending on the value of (winum--check-for-scope-change)
`winum-scope'." (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'."
(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-table)) winum--window-vector))
(defun winum--get-index-table () (defun winum--get-numbers-table ()
"Return the index hashtable used to get an index given a window. "Return the numbers hashtable used to get a number 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--index-table)) winum--numbers-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.
@ -612,12 +593,17 @@ 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)))
@ -643,6 +629,10 @@ 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