From d29c2bee0ebba8e5046e40690c6b70be0d7c932a Mon Sep 17 00:00:00 2001 From: deb0ch Date: Mon, 28 Nov 2016 22:01:35 +0100 Subject: [PATCH] Simplify implementation when frame-local and clarify the code. --- window-numbering.el | 393 ++++++++++++++++++++++++-------------------- 1 file changed, 216 insertions(+), 177 deletions(-) diff --git a/window-numbering.el b/window-numbering.el index 2383778..3cf54fd 100644 --- a/window-numbering.el +++ b/window-numbering.el @@ -49,7 +49,7 @@ (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. +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 @@ -70,7 +70,7 @@ return a number to have it assigned to the current-window, nil otherwise." ;; TODO when changed from frame-local to non-local in customize, need to force ;; update, or `window-numbering-get-number' fails and crashes the modeline ;; until next update. -(defcustom window-numbering-frame-scope 'global +(defcustom window-numbering-scope 'global "The scope of number sets." :group 'window-numbering :type '(choice @@ -80,7 +80,7 @@ return a number to have it assigned to the current-window, nil otherwise." (defcustom window-numbering-reverse-frame-list nil "If t, order frames by reverse order of creation. -Has effect only when `window-numbering-frame-scope' is not 'frame-local." +Has effect only when `window-numbering-scope' is not 'frame-local." :group 'window-numbering :type 'boolean) @@ -102,130 +102,51 @@ Has effect only when `window-numbering-frame-scope' is not 'frame-local." map) "Keymap used in by `window-numbering-mode'.") -(defvar window-numbering--windows nil - "Vector of windows indexed by their number. -Used internally by window-numbering to get a window provided a number.") +;;;###autoload +(define-minor-mode window-numbering-mode + "A minor mode that allows for managing windows based on window numbers." + nil ; initial value of the mode variable + nil ; lighter (name in mode-line) + window-numbering-keymap ; keymap + :global t + (if window-numbering-mode + (window-numbering--init) + (window-numbering--deinit))) -(defvar window-numbering--numbers nil - "Hash table of numbers indexed by their window. -Used internally by window-numbering to get a number provided a window.") +;; define interactive functions window-numbering-select-window-[0..n] +(dotimes (i 10) + (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)))) -(defvar window-numbering--frames-table nil - "Bidirectional table between windows and window numbers. +;;;###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)))) -Used when `window-numbering-frame-scope' is 'frame-local to keep -track of separate window numbers sets in every frame. +;;;###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 10) + (setq window (aref windows i))) + window + (error "No window numbered %s" i)))) -It is a hash table using emacs frames as keys and cons of the form -\(`window-numbering--windows' . `window-numbering--numbers') 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--left nil - "A list of unused window numbers.") - -(defun window-numbering-calculate-left (windows) - "Return a list of numbers currently available for assignment. -WINDOWS: a vector of currently assigned windows." - (let ((i 9) - left) - (while (>= i 0) - (let ((window (aref windows i))) - (unless window - (push (% (1+ i) 10) left))) - (decf i)) - left)) - -(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-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)) - ;; (window-numbering-ignored-p w) ;; TODO implement - ))) - (cl-case window-numbering-frame-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-frame-scope': %S" - window-numbering-frame-scope))))) - -(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--left'. -Returns the assigned number, or nil on error." - (if number - (if (aref window-numbering--windows number) - (progn (message "Number %s assigned to two buffers (%s and %s)" - number window (aref window-numbering--windows number)) - nil) - (setf (aref window-numbering--windows number) window) - (puthash window number window-numbering--numbers) - (setq window-numbering--left (delq number window-numbering--left)) - number) - ;; else determine number and assign - (when window-numbering--left - (unless (gethash window window-numbering--numbers) - (let ((number (car window-numbering--left))) - (window-numbering-assign window number)))))) - -;; TODO update mode-line in all frames -(defun window-numbering-update () - "Update window numbers." - (setq window-numbering--windows (make-vector 10 nil) - window-numbering--numbers (make-hash-table :size 10) - window-numbering--left (window-numbering-calculate-left - window-numbering--windows)) - (when (eq window-numbering-frame-scope 'frame-local) - (puthash (selected-frame) - (cons window-numbering--windows window-numbering--numbers) - window-numbering--frames-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 (window) - (with-selected-window window - (with-current-buffer (window-buffer window) - (let ((num (funcall window-numbering-assign-func))) - (when num - (window-numbering-assign window num)))))) - windows)) - (dolist (window windows) - (window-numbering-assign window)))) - -(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)))) +;; TODO function to select window of unlimited input number: +;; - prefix argument +;; - read-from-minibuffer ;;;###autoload (defun window-numbering-install-mode-line (&optional position) @@ -273,68 +194,186 @@ 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-frame-scope 'frame-local) + (if (eq window-numbering-scope 'frame-local) (gethash w (cdr (gethash (selected-frame) window-numbering--frames-table))) - (gethash w window-numbering--numbers)))) + (gethash w window-numbering--numbers-table)))) -;;;###autoload -(defun get-window-by-number (i) - "Return window numbered I if exists." - (let ((windows (if (eq window-numbering-frame-scope 'frame-local) - (car (gethash (selected-frame) - window-numbering--frames-table)) - window-numbering--windows)) - window) - (if (and (>= i 0) (< i 10) - (setq window (aref windows i))) - window - (error "No window numbered %s" i)))) +;; Internal variables -;;;###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 (get-window-by-number i))) - (if arg - (delete-window w) - (window-numbering-switch-to-window w)))) +(defvar window-numbering--window-number-max 10 ;; TODO replace hard-coded values + ;; by this var + "Max number of windows that can be numbered.") -;; TODO select window of unlimited input number: -;; - prefix argument -;; - read-from-minibuffer +(defvar window-numbering--max-frames-count 16 + "Maximum number of frames that can be numbered.") -;;;###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 - (unless window-numbering--frames-table - (save-excursion +(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." + (unless window-numbering--frames-table + (save-excursion ;; TODO is this really needed ? + (if (eq window-numbering-scope 'frame-local) (setq window-numbering--frames-table (make-hash-table :size 16)) - (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)))) - (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))) + (setq window-numbering--numbers-table (make-hash-table :size 10))) + (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-select-window-[0-9] ()) -(dotimes (i 10) - (eval `(defun ,(intern (format "select-window-%s" i)) (&optional arg) - ,(format "Select the window with number %i." i) - (interactive "P") - (select-window-by-number ,i arg)))) +(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--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--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)) + ;; (window-numbering-ignored-p w) ;; TODO implement + ))) + (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--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--get-available-numbers (&optional windows) + "Return a list of numbers currently available for assignment. +WINDOWS: a vector of currently assigned windows." + ;; TODO simplify, the WINDOWS argument is not needed in the current + ;; implementation. + (let ((i 9) + left) + (while (>= i 0) + (let ((window (when windows (aref windows i)))) + (unless window + (push (% (1+ i) 10) left))) + (decf i)) + left)) + +;; TODO bug: mode-line is sometimes not updated in all visible frames +(defun window-numbering--update () + "Update window numbers." + (setq window-numbering--remaining (window-numbering--get-available-numbers)) + (if (eq window-numbering-scope 'frame-local) + (puthash (selected-frame) + (cons (make-vector 10 nil) (make-hash-table :size 10)) + window-numbering--frames-table) + (setq window-numbering--window-vector (make-vector 10 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--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)