Simplify implementation when frame-local and clarify the code.
This commit is contained in:
parent
fa44f171cf
commit
d29c2bee0e
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in a new issue