From 25f8c31c7555442ffe1ab5e2cca4b5ce699971da Mon Sep 17 00:00:00 2001 From: deb0ch Date: Sat, 3 Dec 2016 01:59:13 +0100 Subject: [PATCH] rename to winum.el keeping backward compatibility --- README.org | 56 +++---- window-numbering.el | 372 +++---------------------------------------- winum.el | 379 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 426 insertions(+), 381 deletions(-) create mode 100644 winum.el diff --git a/README.org b/README.org index 1928613..79c72de 100644 --- a/README.org +++ b/README.org @@ -36,9 +36,9 @@ frames, giving the user a smoother experience of multi-screen Emacs. #+BEGIN_SRC emacs-lisp (add-to-list 'load-path "/path/to/install/folder/winum.el/") - (require 'window-numbering) + (require 'winum) - (window-numbering-mode) + (winum-mode) #+END_SRC @@ -58,17 +58,17 @@ selecting it. * Configuration ** Custom keymap To define your own bindings and override the default ones, override - =window-numbering-keymap= before activating the mode: + =winum-keymap= before activating the mode: #+BEGIN_SRC emacs-lisp - (require 'window-numbering) + (require 'winum) - (setq window-numbering-keymap (let ((map (make-sparse-keymap))) - (define-key map "\M-m 0" 'select-window-0) - ; ... - (define-key map "\M-m 9" 'select-window-9) - map)) - (window-numbering-mode) + (setq winum-keymap (let ((map (make-sparse-keymap))) + (define-key map "\M-m 0" 'select-window-0) + ; ... + (define-key map "\M-m 9" 'select-window-9) + map)) + (winum-mode) #+END_SRC This way your bindings will not error when the mode is turned off. @@ -76,39 +76,39 @@ This way your bindings will not error when the mode is turned off. ** Customize options Several options are available through Emacs' =Customize= interface under -=convenience= > =window-numbering=: +=convenience= > =winum=: -- =window-numbering-scope= +- =winum-scope= Frames affected by a number set. Choices are 'frame-local 'visible or 'global. Default: 'global -- =window-numbering-reverse-frame-list= +- =winum-reverse-frame-list= If t, order frames by reverse order of creation. Has effect only when -`window-numbering-scope' is not 'frame-local. +`winum-scope' is not 'frame-local. Default: nil -- =window-numbering-auto-assign-0-to-minibuffer= +- =winum-auto-assign-0-to-minibuffer= -If non-nil, =window-numbering-mode= assigns 0 to the minibuffer if active. +If non-nil, =winum-mode= assigns 0 to the minibuffer if active. Default: t -- =window-numbering-before-hook= +- =winum-before-hook= -Hook called before =window-numbering-mode= starts assigning numbers. The list of -windows to be numbered is passed as a parameter. Use =window-numbering--assign= +Hook called before =winum-mode= starts assigning numbers. The list of +windows to be numbered is passed as a parameter. Use =winum--assign= to manually assign some of them a number. If you want to assign a number to just -one buffer, use =window-numbering-assign-func= instead. +one buffer, use =winum-assign-func= instead. Default: nil -- =window-numbering-assign-func= +- =winum-assign-func= -Function called for each window by =window-numbering-mode=. This is called +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. @@ -117,29 +117,29 @@ Default: nil Example: always assign the calculator window the number 9: #+BEGIN_SRC emacs-lisp - (setq window-numbering-assign-func + (setq winum-assign-func (lambda () (when (equal (buffer-name) "*Calculator*") 9))) #+END_SRC -- =window-numbering-mode-line-position= +- =winum-mode-line-position= -The position in the mode-line `window-numbering-mode' displays the number. +The position in the mode-line `winum-mode' displays the number. Default: 1 -- =window-numbering-window-number-max= 10 +- =winum-window-number-max= 10 Max number of windows that can be numbered. Default: 10 -- =window-numbering-ignored-buffers= +- =winum-ignored-buffers= List of buffers to ignore when selecting window. Default: '(" *which-key*") -- face: =window-numbering-face= +- face: =winum-face= Face used for the number in the mode-line. diff --git a/window-numbering.el b/window-numbering.el index 48cb992..b649767 100644 --- a/window-numbering.el +++ b/window-numbering.el @@ -1,6 +1,5 @@ -;;; winum.el --- Window numbers for Emacs. An extended version of window-numbering.el, ideal for multi-screen. +;;; window-numbering.el --- Compatibility code for window-numbering configurations ;; -;; Copyright (c) 2006-2015 Nikolaj Schumacher ;; Copyright (c) 2016 Thomas Chauvot de Beauchêne ;; ;; This program is free software: you can redistribute it and/or modify @@ -16,364 +15,31 @@ ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;; -;; Author: Thomas de Beauchêne -;; Version: 1.0 -;; Keywords: windows, window management, numbers -;; URL: http://github.com/deb0ch/winum.el -;; Created: 2016 -;; Compatibility: GNU Emacs 24.x -;; -;; This file is NOT part of GNU Emacs. -;; ;;; Commentary: ;; -;; Window numbers for Emacs: Navigate your windows and frames using numbers. +;; Ensure compatibility with existing window-numbering configurations. ;; -;; This package is an extended and actively maintained version of the -;; https://github.com/nschum/window-numbering.el package by Nikolaj Schumacher, -;; with some ideas and code taken from https://github.com/abo-abo/ace-window. +;; This file exists for transitional purposes and will be deleted in the next +;; updates. ;; ;;; Code: -(eval-when-compile (require 'cl)) +(require 'winum) -;; TODO rename to 'winum.el' and keep backward compatibility +(defalias 'window-numbering-get-number-string 'winum-get-number-string) +(defalias 'window-numbering-get-window-by-number 'winum-get-window-by-number) +(defalias 'window-numbering-get-number 'winum-get-number) +(defalias 'window-numbering-mode 'winum-mode) -(defgroup window-numbering nil - "Navigate and manage windows using numbers." - :group 'convenience) - -;; TODO bug: when changed from frame-local to non-local in customize, need to -;; force update or `window-numbering-get-number' fails and messes the -;; modeline until next update. -(defcustom window-numbering-scope 'global - "Frames affected by a number set." - :group 'window-numbering - :type '(choice - (const :tag "frame local" frame-local) - (const :tag "visible frames" visible) - (const :tag "global" global))) - -(defcustom window-numbering-reverse-frame-list nil - "If t, order frames by reverse order of creation. -Has effect only when `window-numbering-scope' is not 'frame-local." - :group 'window-numbering - :type 'boolean) - -(defcustom window-numbering-auto-assign-0-to-minibuffer t - "If non-nil, `window-numbering-mode' assigns 0 to the minibuffer if active." - :group 'window-numbering - :type 'boolean) - -;; TODO see if useful -(defcustom window-numbering-before-hook nil - "Hook called before `window-numbering-mode' starts assigning numbers. -The list of windows to be numbered is passed as a parameter. -Use `window-numbering--assign' to manually assign some of them a number. -If you want to assign a number to just one buffer, use -`window-numbering-assign-func' instead." - :group 'window-numbering - :type 'hook) - -(defcustom window-numbering-assign-func nil - "Function called for each window by `window-numbering-mode'. -This is called before automatic assignment begins. The function should -return a number to have it assigned to the current-window, nil otherwise." - :group 'window-numbering - :type 'function) - -(defcustom window-numbering-mode-line-position 1 - "The position in the mode-line `window-numbering-mode' displays the number." - :group 'window-numbering - :type 'integer) - -(defcustom window-numbering-window-number-max 10 - "Max number of windows that can be numbered." - :group 'window-numbering - :type 'integer) - -(defcustom window-numbering-ignored-buffers '(" *which-key*") - "List of buffers to ignore when selecting window." - :type '(repeat string)) - -(defface window-numbering-face '() - "Face used for the number in the mode-line." - :group 'window-numbering) - -(defvar window-numbering-keymap (let ((map (make-sparse-keymap))) - (define-key map "\M-0" 'select-window-0) - (define-key map "\M-1" 'select-window-1) - (define-key map "\M-2" 'select-window-2) - (define-key map "\M-3" 'select-window-3) - (define-key map "\M-4" 'select-window-4) - (define-key map "\M-5" 'select-window-5) - (define-key map "\M-6" 'select-window-6) - (define-key map "\M-7" 'select-window-7) - (define-key map "\M-8" 'select-window-8) - (define-key map "\M-9" 'select-window-9) - map) - "Keymap used in by `window-numbering-mode'.") - -;;;###autoload -(define-minor-mode window-numbering-mode - "A minor mode that allows for managing windows based on window numbers." - nil - nil - window-numbering-keymap - :global t - (if window-numbering-mode - (window-numbering--init) - (window-numbering--deinit))) - -;; define interactive functions window-numbering-select-window-[0..n] -(dotimes (i (max 10 window-numbering-window-number-max)) - (eval `(defun ,(intern (format "select-window-%s" i)) (&optional arg) - ,(format "Jump to window %d.\nIf prefix ARG is given, delete the\ - window instead of selecting it." i) - (interactive "P") - (select-window-by-number ,i arg)))) - -;;;###autoload -(defun select-window-by-number (i &optional arg) - "Select window given number I by `window-numbering-mode'. -If prefix ARG is given, delete the window instead of selecting it." - (interactive "P") - (let ((w (window-numbering-get-window-by-number i))) - (if arg - (delete-window w) - (window-numbering--switch-to-window w)))) - -;;;###autoload -(defun window-numbering-get-window-by-number (i) - "Return window numbered I if exists." - (let ((windows (if (eq window-numbering-scope 'frame-local) - (car (gethash (selected-frame) - window-numbering--frames-table)) - window-numbering--window-vector)) - window) - (if (and (>= i 0) (< i window-numbering-window-number-max) - (setq window (aref windows i))) - window - (error "No window numbered %s" i)))) - -;; TODO function to select window of unlimited input number: -;; - prefix argument -;; - read-from-minibuffer - -;;;###autoload -(defun window-numbering-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 ((s (int-to-string (window-numbering-get-number window)))) - (propertize s 'face 'window-numbering-face))) - -;;;###autoload -(defun window-numbering-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)))) - (if (eq window-numbering-scope 'frame-local) - (gethash w (cdr (gethash (selected-frame) - window-numbering--frames-table))) - (gethash w window-numbering--numbers-table)))) - -;; Internal variables - -(defvar window-numbering--max-frames 16 - "Maximum number of frames that can be numbered.") - -(defvar window-numbering--window-vector nil - "Vector of windows indexed by their number. -Used internally by window-numbering to get a window provided a number.") - -(defvar window-numbering--numbers-table nil - "Hash table of numbers indexed by their window. -Used internally by window-numbering to get a number provided a window.") - -(defvar window-numbering--frames-table nil - "Table linking windows to numbers and numbers to windows for each frame. - -Used only when `window-numbering-scope' is 'frame-local to keep track of -separate window numbers sets in every frame. - -It is a hash table using Emacs frames as keys and cons of the form -\(`window-numbering--window-vector' . `window-numbering--numbers-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. - -Such a structure allows for per-frame bidirectional fast access.") - -(defvar window-numbering--remaining nil - "A list of available window numbers.") - -(defun window-numbering--init () - "Initialize window-numbering-mode." - (if (eq window-numbering-scope 'frame-local) - (setq window-numbering--frames-table (make-hash-table :size window-numbering--max-frames)) - (setq window-numbering--numbers-table (make-hash-table :size window-numbering-window-number-max))) - (window-numbering--install-mode-line) - (add-hook 'minibuffer-setup-hook 'window-numbering--update) - (add-hook 'window-configuration-change-hook 'window-numbering--update) - (dolist (frame (frame-list)) - (select-frame frame) - (window-numbering--update))) - -(defun window-numbering--deinit () - "Actions performed when turning off window-numbering-mode." - (window-numbering--clear-mode-line) - (remove-hook 'minibuffer-setup-hook 'window-numbering--update) - (remove-hook 'window-configuration-change-hook 'window-numbering--update) - (setq window-numbering--frames-table nil)) - -(defun window-numbering--install-mode-line (&optional position) - "Install the window number from `window-numbering-mode' to the mode-line. -POSITION: position in the mode-line." - (let ((mode-line (default-value 'mode-line-format)) - (res)) - (dotimes (i (min (or position window-numbering-mode-line-position 1) - (length mode-line))) - (push (car mode-line) res) - (pop mode-line)) - (push '(:eval (window-numbering-get-number-string)) res) - (while mode-line - (push (car mode-line) res) - (pop mode-line)) - (setq-default mode-line-format (nreverse res))) - (force-mode-line-update t)) - -(defun window-numbering--clear-mode-line () - "Remove the window number of `window-numbering-mode' from the mode-line." - (let ((mode-line (default-value 'mode-line-format)) - (res)) - (while mode-line - (let ((item (car mode-line))) - (unless (equal item '(:eval (window-numbering-get-number-string))) - (push item res))) - (pop mode-line)) - (setq-default mode-line-format (nreverse res))) - (force-mode-line-update t)) - -(defun window-numbering--update () - "Update window numbers." - (setq window-numbering--remaining (window-numbering--available-numbers)) - (if (eq window-numbering-scope 'frame-local) - (puthash (selected-frame) - (cons (make-vector window-numbering-window-number-max nil) - (make-hash-table :size window-numbering-window-number-max)) - window-numbering--frames-table) - (setq window-numbering--window-vector (make-vector window-numbering-window-number-max nil)) - (clrhash window-numbering--numbers-table)) - (when (and window-numbering-auto-assign-0-to-minibuffer - (active-minibuffer-window)) - (window-numbering--assign (active-minibuffer-window) 0)) - (let ((windows (window-numbering--window-list))) - (run-hook-with-args 'window-numbering-before-hook windows) - (when window-numbering-assign-func - (mapc (lambda (w) - (with-selected-window w - (with-current-buffer (window-buffer w) - (let ((num (funcall window-numbering-assign-func))) - (when num - (window-numbering--assign w num)))))) - windows)) - (dolist (w windows) - (window-numbering--assign w)))) - -(defun window-numbering--assign (window &optional number) - "Assign to window WINDOW the number NUMBER. -If NUMBER is not specified, determine it first based on -`window-numbering--remaining'. -Returns the assigned number, or nil on error." - (if number - (if (aref (window-numbering--get-window-vector) number) - (progn (message "Number %s assigned to two buffers (%s and %s)" - number window (aref window-numbering--window-vector number)) - nil) - (setf (aref (window-numbering--get-window-vector) number) window) - (puthash window number (window-numbering--get-numbers-table)) - (setq window-numbering--remaining (delq number window-numbering--remaining)) - number) - ;; else determine number and assign - (when window-numbering--remaining - (unless (gethash window (window-numbering--get-numbers-table)) - (let ((number (car window-numbering--remaining))) - (window-numbering--assign window number)))))) - -(defun window-numbering--window-list () - "Return a list of interesting windows." - (cl-remove-if - (lambda (w) - (let ((f (window-frame w))) - (or (not (and (frame-live-p f) - (frame-visible-p f))) - (string= "initial_terminal" (terminal-name f)) - (member (buffer-name (window-buffer w)) window-numbering-ignored-buffers)))) - (cl-case window-numbering-scope - (global - (cl-mapcan 'window-numbering--list-windows-in-frame - (if window-numbering-reverse-frame-list - (frame-list) - (nreverse (frame-list))))) - (visible - (cl-mapcan 'window-numbering--list-windows-in-frame - (if window-numbering-reverse-frame-list - (visible-frame-list) - (nreverse (visible-frame-list))))) - (frame-local - (window-numbering--list-windows-in-frame)) - (t - (error "Invalid `window-numbering-scope': %S" window-numbering-scope))))) - -(defun window-numbering--list-windows-in-frame (&optional f) - "List windows in frame F using natural Emacs ordering." - (window-list f 0 (frame-first-window f))) - -(defun window-numbering--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 -`window-numbering-scope'." - (if (eq window-numbering-scope 'frame-local) - (car (gethash (selected-frame) - window-numbering--frames-table)) - window-numbering--window-vector)) - -(defun window-numbering--get-numbers-table () - "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 -`window-numbering-scope'" - (if (eq window-numbering-scope 'frame-local) - (cdr (gethash (selected-frame) - window-numbering--frames-table)) - window-numbering--numbers-table)) - -(defun window-numbering--available-numbers () - "Return a list of numbers from 1 to `window-numbering-window-number-max'. -0 is the last element of the list." - (let ((numbers)) - (dotimes (i window-numbering-window-number-max) - (push (% (1+ i) window-numbering-window-number-max) numbers)) - (nreverse numbers))) - -(defun window-numbering--switch-to-window (window) - "Switch to the window WINDOW and switch input focus if on a different frame." - (let ((frame (window-frame window))) - (when (and (frame-live-p frame) - (not (eq frame (selected-frame)))) - (select-frame-set-input-focus frame)) - (if (window-live-p window) - (select-window window) - (error "Got a dead window %S" window)))) - -(push "^No window numbered .$" debug-ignored-errors) -(push "^Got a dead window .$" debug-ignored-errors) -(push "^Invalid `window-numbering-scope': .$" debug-ignored-errors) +(defvaralias 'window-numbering-mode 'winum-mode) +(defvaralias 'window-numbering-keymap 'winum-keymap) +(defvaralias 'window-numbering-ignored-buffers 'winum-ignored-buffers) +(defvaralias 'window-numbering-mode-line-position 'winum-mode-line-position) +(defvaralias 'window-numbering-before-hook 'winum-before-hook) +(defvaralias 'window-numbering-scope 'winum-scope) +(defvaralias 'window-numbering-auto-assign-0-to-minibuffer 'winum-auto-assign-0-to-minibuffer) +(defvaralias 'window-numbering-assign-func 'winum-assign-func) +(defvaralias 'window-numbering-window-number-max 'winum-window-number-max) +(defvaralias 'window-numbering-reverse-frame-list 'winum-reverse-frame-list) (provide 'window-numbering) - -;;; window-numbering.el ends here diff --git a/winum.el b/winum.el new file mode 100644 index 0000000..095da65 --- /dev/null +++ b/winum.el @@ -0,0 +1,379 @@ +;;; winum.el --- Window numbers for Emacs. An extended version of window-numbering.el, ideal for multi-screen. +;; +;; Copyright (c) 2006-2015 Nikolaj Schumacher +;; Copyright (c) 2016 Thomas Chauvot de Beauchêne +;; +;; 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 +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . +;; +;; Author: Thomas de Beauchêne +;; Version: 1.0 +;; Keywords: windows, window management, numbers +;; URL: http://github.com/deb0ch/winum.el +;; Created: 2016 +;; Compatibility: GNU Emacs 24.x +;; +;; This file is NOT part of GNU Emacs. +;; +;;; Commentary: +;; +;; 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 package by Nikolaj Schumacher, +;; with some ideas and code taken from https://github.com/abo-abo/ace-window. +;; +;;; Code: + +(eval-when-compile (require 'cl)) + +;; TODO make mode-line installation optional + +;; TODO bug: Error during redisplay: (eval (winum-get-number-string)) signaled +;; (wrong-type-argument numberp nil) when opening a helm buffer. + +(defgroup winum nil + "Navigate and manage windows using numbers." + :group 'convenience) + +;; TODO bug: when changed from frame-local to non-local in customize, need to +;; force update or `winum-get-number' fails and messes the +;; modeline until next update. +(defcustom winum-scope 'global + "Frames affected by a number set." + :group 'winum + :type '(choice + (const :tag "frame local" frame-local) + (const :tag "visible frames" visible) + (const :tag "global" global))) + +(defcustom winum-reverse-frame-list nil + "If t, order frames by reverse order of creation. +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 if active." + :group 'winum + :type 'boolean) + +;; TODO see if useful +(defcustom winum-before-hook nil + "Hook called before `winum-mode' starts assigning numbers. +The list of windows to be numbered is passed as a parameter. +Use `winum--assign' to manually assign some of them a number. +If you want to assign a number to just one buffer, use +`winum-assign-func' instead." + :group 'winum + :type 'hook) + +(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." + :group 'winum + :type 'function) + +(defcustom winum-mode-line-position 1 + "The position in the mode-line `winum-mode' displays the number." + :group 'winum + :type 'integer) + +;; TODO other values note tested +(defcustom winum-window-number-max 10 + "Max number of windows that can be numbered." + :group 'winum + :type 'integer) + +(defcustom winum-ignored-buffers '(" *which-key*") + "List of buffers to ignore when selecting window." + :type '(repeat string)) + +(defface winum-face '() + "Face used for the number in the mode-line." + :group 'winum) + +(defvar winum-keymap (let ((map (make-sparse-keymap))) + (define-key map "\M-0" 'select-window-0) + (define-key map "\M-1" 'select-window-1) + (define-key map "\M-2" 'select-window-2) + (define-key map "\M-3" 'select-window-3) + (define-key map "\M-4" 'select-window-4) + (define-key map "\M-5" 'select-window-5) + (define-key map "\M-6" 'select-window-6) + (define-key map "\M-7" 'select-window-7) + (define-key map "\M-8" 'select-window-8) + (define-key map "\M-9" 'select-window-9) + map) + "Keymap used in by `winum-mode'.") + +;;;###autoload +(define-minor-mode winum-mode + "A minor mode that allows for managing windows based on window numbers." + nil + nil + winum-keymap + :global t + (if winum-mode + (winum--init) + (winum--deinit))) + +;; define interactive functions winum-select-window-[0..n] +(dotimes (i winum-window-number-max) + (eval `(defun ,(intern (format "select-window-%s" i)) (&optional arg) + ,(format "Jump to window %d.\nIf prefix ARG is given, delete the\ + window instead of selecting it." i) + (interactive "P") + (select-window-by-number ,i arg)))) + +;;;###autoload +(defun select-window-by-number (i &optional arg) + "Select window given number I by `winum-mode'. +If prefix ARG is given, delete the window instead of selecting it." + (interactive "P") + (let ((w (winum-get-window-by-number i))) + (if arg + (delete-window w) + (winum--switch-to-window w)))) + +;;;###autoload +(defun winum-get-window-by-number (n) + "Return window numbered N if exists." + (let ((windows (if (eq winum-scope 'frame-local) + (car (gethash (selected-frame) + winum--frames-table)) + winum--window-vector)) + window) + (if (and (>= n 0) (< n winum-window-number-max) + (setq window (aref windows n))) + window + (error "No window numbered %s" n)))) + +;;;###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 ((s (int-to-string (winum-get-number window)))) + (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)))) + (if (eq winum-scope 'frame-local) + (gethash w (cdr (gethash (selected-frame) + winum--frames-table))) + (gethash w winum--numbers-table)))) + +;; Internal variables + +(defvar winum--max-frames 16 + "Maximum number of frames that can be numbered.") + +(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--frames-table nil + "Table linking windows to numbers and numbers to windows for each frame. + +Used only when `winum-scope' is 'frame-local to keep track of +separate window numbers 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') +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. + +Such a structure allows for per-frame bidirectional fast access.") + +(defvar winum--remaining nil + "A list of available window numbers.") + +(defun winum--init () + "Initialize winum-mode." + (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-number-max))) + (winum--install-mode-line) + (add-hook 'minibuffer-setup-hook 'winum--update) + (add-hook 'window-configuration-change-hook 'winum--update) + (dolist (frame (frame-list)) + (select-frame frame) + (winum--update))) + +(defun winum--deinit () + "Actions performed when turning off winum-mode." + (winum--clear-mode-line) + (remove-hook 'minibuffer-setup-hook 'winum--update) + (remove-hook 'window-configuration-change-hook 'winum--update) + (setq winum--frames-table nil)) + +(defun winum--install-mode-line (&optional position) + "Install the window number from `winum-mode' to the mode-line. +POSITION: position in the mode-line." + (let ((mode-line (default-value 'mode-line-format)) + (res)) + (dotimes (i (min (or position winum-mode-line-position 1) + (length mode-line))) + (push (car mode-line) res) + (pop mode-line)) + (push '(:eval (winum-get-number-string)) res) + (while mode-line + (push (car mode-line) res) + (pop mode-line)) + (setq-default mode-line-format (nreverse res))) + (force-mode-line-update t)) + +(defun winum--clear-mode-line () + "Remove the window number of `winum-mode' from the mode-line." + (let ((mode-line (default-value 'mode-line-format)) + (res)) + (while mode-line + (let ((item (car mode-line))) + (unless (equal item '(:eval (winum-get-number-string))) + (push item res))) + (pop mode-line)) + (setq-default mode-line-format (nreverse res))) + (force-mode-line-update t)) + +(defun winum--update () + "Update window numbers." + (setq winum--remaining (winum--available-numbers)) + (if (eq winum-scope 'frame-local) + (puthash (selected-frame) + (cons (make-vector winum-window-number-max nil) + (make-hash-table :size winum-window-number-max)) + winum--frames-table) + (setq winum--window-vector (make-vector winum-window-number-max nil)) + (clrhash winum--numbers-table)) + (when (and winum-auto-assign-0-to-minibuffer + (active-minibuffer-window)) + (winum--assign (active-minibuffer-window) 0)) + (let ((windows (winum--window-list))) + (run-hook-with-args 'winum-before-hook windows) + (when winum-assign-func + (mapc (lambda (w) + (with-selected-window w + (with-current-buffer (window-buffer w) + (let ((num (funcall winum-assign-func))) + (when num + (winum--assign w num)))))) + windows)) + (dolist (w windows) + (winum--assign w)))) + +(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 + (if (aref (winum--get-window-vector) number) + (progn (message "Number %s assigned to two buffers (%s and %s)" + number window (aref winum--window-vector number)) + 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--window-list () + "Return a list of interesting windows." + (cl-remove-if + (lambda (w) + (let ((f (window-frame w))) + (or (not (and (frame-live-p f) + (frame-visible-p f))) + (string= "initial_terminal" (terminal-name f)) + (member (buffer-name (window-buffer w)) winum-ignored-buffers)))) + (cl-case winum-scope + (global + (cl-mapcan 'winum--list-windows-in-frame + (if winum-reverse-frame-list + (frame-list) + (nreverse (frame-list))))) + (visible + (cl-mapcan 'winum--list-windows-in-frame + (if winum-reverse-frame-list + (visible-frame-list) + (nreverse (visible-frame-list))))) + (frame-local + (winum--list-windows-in-frame)) + (t + (error "Invalid `winum-scope': %S" winum-scope))))) + +(defun winum--list-windows-in-frame (&optional f) + "List windows in frame F using natural Emacs ordering." + (window-list f 0 (frame-first-window f))) + +(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'." + (if (eq winum-scope 'frame-local) + (car (gethash (selected-frame) + winum--frames-table)) + winum--window-vector)) + +(defun winum--get-numbers-table () + "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 +`winum-scope'" + (if (eq winum-scope 'frame-local) + (cdr (gethash (selected-frame) + winum--frames-table)) + winum--numbers-table)) + +(defun winum--available-numbers () + "Return a list of numbers from 1 to `winum-window-number-max'. +0 is the last element of the list." + (let ((numbers)) + (dotimes (i winum-window-number-max) + (push (% (1+ i) winum-window-number-max) 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))) + (when (and (frame-live-p frame) + (not (eq frame (selected-frame)))) + (select-frame-set-input-focus frame)) + (if (window-live-p window) + (select-window window) + (error "Got a dead window %S" window)))) + +(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