Simplify implementation when frame-local and clarify the code.

This commit is contained in:
deb0ch 2016-11-28 22:01:35 +01:00
parent fa44f171cf
commit d29c2bee0e

View file

@ -49,7 +49,7 @@
(defcustom window-numbering-before-hook nil (defcustom window-numbering-before-hook nil
"Hook called before `window-numbering-mode' starts assigning numbers. "Hook called before `window-numbering-mode' starts assigning numbers.
The list of windows to be numbered is passed as a parameter. 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 If you want to assign a number to just one buffer, use
`window-numbering-assign-func' instead." `window-numbering-assign-func' instead."
:group 'window-numbering :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 ;; 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 ;; update, or `window-numbering-get-number' fails and crashes the modeline
;; until next update. ;; until next update.
(defcustom window-numbering-frame-scope 'global (defcustom window-numbering-scope 'global
"The scope of number sets." "The scope of number sets."
:group 'window-numbering :group 'window-numbering
:type '(choice :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 (defcustom window-numbering-reverse-frame-list nil
"If t, order frames by reverse order of creation. "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 :group 'window-numbering
:type 'boolean) :type 'boolean)
@ -102,130 +102,51 @@ Has effect only when `window-numbering-frame-scope' is not 'frame-local."
map) map)
"Keymap used in by `window-numbering-mode'.") "Keymap used in by `window-numbering-mode'.")
(defvar window-numbering--windows nil ;;;###autoload
"Vector of windows indexed by their number. (define-minor-mode window-numbering-mode
Used internally by window-numbering to get a window provided a number.") "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 ;; define interactive functions window-numbering-select-window-[0..n]
"Hash table of numbers indexed by their window. (dotimes (i 10)
Used internally by window-numbering to get a number provided a window.") (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 ;;;###autoload
"Bidirectional table between windows and window numbers. (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 ;;;###autoload
track of separate window numbers sets in every frame. (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 ;; TODO function to select window of unlimited input number:
\(`window-numbering--windows' . `window-numbering--numbers') as values. ;; - prefix argument
;; - read-from-minibuffer
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))))
;;;###autoload ;;;###autoload
(defun window-numbering-install-mode-line (&optional position) (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 If not specified, the number of the currently selected window is
returned." returned."
(let ((w (or window (selected-window)))) (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) (gethash w (cdr (gethash (selected-frame)
window-numbering--frames-table))) window-numbering--frames-table)))
(gethash w window-numbering--numbers)))) (gethash w window-numbering--numbers-table))))
;;;###autoload ;; Internal variables
(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))))
;;;###autoload (defvar window-numbering--window-number-max 10 ;; TODO replace hard-coded values
(defun select-window-by-number (i &optional arg) ;; by this var
"Select window given number I by `window-numbering-mode'. "Max number of windows that can be numbered.")
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))))
;; TODO select window of unlimited input number: (defvar window-numbering--max-frames-count 16
;; - prefix argument "Maximum number of frames that can be numbered.")
;; - read-from-minibuffer
;;;###autoload (defvar window-numbering--window-vector nil
(define-minor-mode window-numbering-mode "Vector of windows indexed by their number.
"A minor mode that allows for managing windows based on window numbers." Used internally by window-numbering to get a window provided a number.")
nil
nil (defvar window-numbering--numbers-table nil
window-numbering-keymap "Hash table of numbers indexed by their window.
:global t Used internally by window-numbering to get a number provided a window.")
(if window-numbering-mode
(unless window-numbering--frames-table (defvar window-numbering--frames-table nil
(save-excursion "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)) (setq window-numbering--frames-table (make-hash-table :size 16))
(window-numbering-install-mode-line) (setq window-numbering--numbers-table (make-hash-table :size 10)))
(add-hook 'minibuffer-setup-hook 'window-numbering-update) (window-numbering-install-mode-line)
(add-hook 'window-configuration-change-hook (add-hook 'minibuffer-setup-hook 'window-numbering--update)
'window-numbering-update) (add-hook 'window-configuration-change-hook
(dolist (frame (frame-list)) 'window-numbering--update)
(select-frame frame) (dolist (frame (frame-list))
(window-numbering-update)))) (select-frame frame)
(window-numbering-clear-mode-line) (window-numbering--update)))))
(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-select-window-[0-9] ()) (defun window-numbering--deinit ()
(dotimes (i 10) "Actions performed when turning off window-numbering-mode."
(eval `(defun ,(intern (format "select-window-%s" i)) (&optional arg) (window-numbering-clear-mode-line)
,(format "Select the window with number %i." i) (remove-hook 'minibuffer-setup-hook 'window-numbering--update)
(interactive "P") (remove-hook 'window-configuration-change-hook
(select-window-by-number ,i arg)))) '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) (push "^No window numbered .$" debug-ignored-errors)