* [GNU ELPA] I'd like to add switchy.el: a last-recently-used window switcher @ 2023-04-09 7:36 Tassilo Horn 2023-04-09 8:08 ` Eli Zaretskii 2023-04-09 8:56 ` Philip Kaludercic 0 siblings, 2 replies; 6+ messages in thread From: Tassilo Horn @ 2023-04-09 7:36 UTC (permalink / raw) To: emacs-devel [-- Attachment #1: Type: text/plain, Size: 646 bytes --] Hi all, I've written a small utility called switchy.el which I'd like to add to GNU ELPA. I can do that on my own but wanted to give you a chance for commenting first. I'm attaching the file below. In essence, the single command provided is `switchy-window' which is similar to `other-window' except that it switches to other windows in last-recently-used order instead of top-to-bottom-left-to-right order. With quick consecutive invocations you can reach any window but if you have a small delay between invocations (controlled by the single defcustom `switchy-delay'), you toggle between the two last recently used windows. Bye, Tassilo [-- Attachment #2: switchy.el --] [-- Type: application/emacs-lisp, Size: 8075 bytes --] ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [GNU ELPA] I'd like to add switchy.el: a last-recently-used window switcher 2023-04-09 7:36 [GNU ELPA] I'd like to add switchy.el: a last-recently-used window switcher Tassilo Horn @ 2023-04-09 8:08 ` Eli Zaretskii 2023-04-09 8:56 ` Philip Kaludercic 1 sibling, 0 replies; 6+ messages in thread From: Eli Zaretskii @ 2023-04-09 8:08 UTC (permalink / raw) To: Tassilo Horn; +Cc: emacs-devel > From: Tassilo Horn <tsdh@gnu.org> > Date: Sun, 09 Apr 2023 09:36:27 +0200 > > I've written a small utility called switchy.el which I'd like to add to > GNU ELPA. I can do that on my own but wanted to give you a chance for > commenting first. I'm attaching the file below. > > In essence, the single command provided is `switchy-window' which is > similar to `other-window' except that it switches to other windows in > last-recently-used order instead of top-to-bottom-left-to-right order. > With quick consecutive invocations you can reach any window but if you > have a small delay between invocations (controlled by the single > defcustom `switchy-delay'), you toggle between the two last recently > used windows. Thanks. Any chance of changing the name of the package to make the fact that it switches windows more evident? Like switchy-window.el, for example? Another minor nit: you say "last-recently-used order", but I believe the correct terminology is "least-recently-used order". ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [GNU ELPA] I'd like to add switchy.el: a last-recently-used window switcher 2023-04-09 7:36 [GNU ELPA] I'd like to add switchy.el: a last-recently-used window switcher Tassilo Horn 2023-04-09 8:08 ` Eli Zaretskii @ 2023-04-09 8:56 ` Philip Kaludercic 2023-04-09 9:33 ` Tassilo Horn 1 sibling, 1 reply; 6+ messages in thread From: Philip Kaludercic @ 2023-04-09 8:56 UTC (permalink / raw) To: Tassilo Horn; +Cc: emacs-devel Tassilo Horn <tsdh@gnu.org> writes: > Hi all, > > I've written a small utility called switchy.el which I'd like to add to > GNU ELPA. I can do that on my own but wanted to give you a chance for > commenting first. I'm attaching the file below. > > In essence, the single command provided is `switchy-window' which is > similar to `other-window' except that it switches to other windows in > last-recently-used order instead of top-to-bottom-left-to-right order. > With quick consecutive invocations you can reach any window but if you > have a small delay between invocations (controlled by the single > defcustom `switchy-delay'), you toggle between the two last recently > used windows. > > Bye, > Tassilo > > ;;; switchy.el --- A last-recently-used window switcher -*- lexical-binding: t; -*- > ;; > ;; Copyright (C) 2023 Tassilo Horn > ;; > ;; Author: Tassilo Horn <tsdh@gnu.org> > ;; Version: 1.0 > ;; Keywords: windows > ;; Homepage: https://sr.ht/~tsdh/switchy/ > ;; Repository: https://git.sr.ht/~tsdh/switchy > ;; Package-Requires: ((emacs "25.1") (compat "29.1.3.4")) If you are to use compat 29.1.0.0 or newer then you also have to require it! > ;; SPDX-License-Identifier: GPL-3.0-or-later > ;; > ;; This file is NOT part of GNU Emacs. If added to GNU ELPA, this should be change to "... is part of GNU Emacs", right? > ;; > ;; 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 <http://www.gnu.org/licenses/>. > ;; > ;;; Commentary: > ;; > ;; Switchy is a last-recently-used window switcher. It suits my personal Emacs > ;; layout and workflow where I usually have at most two editing windows but up > ;; to three side-windows which I have to select only seldomly. > ;; > ;; The idea of switchy is simple: when you invoke `switchy-window' in quick > ;; succession, it will switch to one window after the other in > ;; last-recently-used order. Once you stop switching for long enough time > ;; (`switchy-delay', 1.5 seconds by default), the selected window gets locked > ;; in, i.e., its LRU timestamp is updated and this switching sequence is ended. > ;; Thusly, you can toggle between two windows simply by invoking > ;; `switchy-window', waiting at least `switchy-delay', and then invoking > ;; `switchy-window' again to switch back to the original window. > ;; > ;; Activate `switchy-minor-mode' which tracks window changes and bind > ;; `switchy-window' to a key of your liking in `switchy-minor-mode-map' (or > ;; globally, see the variable's docstring for examples). > ;; > ;; Hint: Since the order of window switching is not as obvious as it is with > ;; `other-window', adding a bit visual feedback to window selection changes can > ;; be helpful. That can be done easily with the stock Emacs pulse.el, e.g.: > ;; > ;; (add-hook 'window-selection-change-functions > ;; (lambda (frame) > ;; (when (eq frame (selected-frame)) > ;; (pulse-momentary-highlight-one-line)))) > > > ;;; Code: > > (defgroup switchy nil > "Switchy is a last-recently-used window-switcher." > :group 'windows) > > (defvar switchy--tick-counter 0 > "Values of this counter represent the last-recently-used order of windows. > Only for internal use.") > > (defvar switchy--tick-alist nil > "An alist with entries (WINDOW . TICK). > A higher TICK value means a window has more recently been visited. > Only for internal use.") > > (defcustom switchy-delay 1.5 > "Number of seconds before the current window gets locked in. > If more time elapses between consecutive invocations of > `switchy-window', the current window's tick (timestamp) is > updated in `switchy--tick-alist' and the current switching cycle > ends." > :type 'number) > > (defvar switchy--timer nil > "The timer locking in the current window after `switchy-delay' seconds. > Only for internal use.") > > (defvar switchy--visited-windows nil > "The windows having already been visited in the current switching cycle.") > > (defun switchy--on-window-selection-change (&optional frame) > "Record the next `switchy--tick-counter' value for the selected window of FRAME. > Meant to be used in `window-selection-change-functions' which is > arranged by `switchy-minor-mode'." > (when (eq frame (selected-frame)) > (when switchy--timer > (cancel-timer switchy--timer)) > (setq switchy--timer (run-at-time > switchy-delay nil > (lambda () > (setf (alist-get (selected-window) > switchy--tick-alist) > (cl-incf switchy--tick-counter)) > (setq switchy--visited-windows nil)))))) > > (defun switchy-window (&optional arg) > "Switch to other windows in last-recently-used order. > If prefix ARG is given, use least-recently-used order. > > If the time between consecutive invocations is smaller than > `switchy-delay' seconds, selects one after the other window in > LRU order and cycles when all windows have been visited. If > `switchy-delay' has passed, the current switching cycle ends and > the now selected window gets its tick updated (a kind of > timestamp)." > (interactive) > > (unless switchy-minor-mode > (user-error "switchy-window requires `switchy-minor-mode' being active")) > > ;; Remove dead windows. > (setq switchy--tick-alist (seq-filter > (lambda (e) > (window-live-p (car e))) > switchy--tick-alist)) > ;; Add windows never selected. > (dolist (win (window-list (selected-frame))) > (unless (assq win switchy--tick-alist) > (setf (alist-get win switchy--tick-alist) 0))) The setf is strictly speaking unnecessary here and causes an accidental O(n^2) slowdown, since you traverse the list once to check if it has an entry and then traverse it again to check if you can set 0 to an existing entry. You could also just push a cons-cell to the beginning. Then again, this is all bounded by the maximal number of windows that someone has open so it doesn't matter in practice. > ;; Ensure the current window is marked as visited. > (setq switchy--visited-windows (cons (selected-window) > switchy--visited-windows)) > > (let ((win-entries (seq-filter > (lambda (e) > (let ((win (car e))) > (and (eq (window-frame win) (selected-frame)) > (or (minibuffer-window-active-p win) > (not (eq win (minibuffer-window > (selected-frame))))) > (not (memq win switchy--visited-windows))))) > switchy--tick-alist))) > (if win-entries > (when-let ((win (car (seq-reduce (lambda (x e) > (if (and x (funcall (if arg #'< #'>) > (cdr x) (cdr e))) > x > e)) > win-entries nil)))) > > (progn Why the progn if you are using when-let? > (setq switchy--visited-windows (cons win switchy--visited-windows)) > (select-window win))) > ;; Start a new cycle if we're not at the start already, i.e., we visited > ;; just one (the current) window. > (when (> (length switchy--visited-windows) 1) Or (length> switchy--visited-windows 1)? > (setq switchy--visited-windows nil) > (switchy-window))))) > > (defvar switchy-minor-mode-map (make-sparse-keymap) > "The mode map of `switchy-minor-mode'. > No keys are bound by default. Bind the main command > `switchy-window' to a key of your liking, e.g., > > ;; That\\='s what I use. > (keymap-set switchy-minor-mode-map \"C-<\" #\\='switchy-window) If you are already making use of keymap-set, you might as well define the map itself using defvar-keymap (Compat provides it). > ;; Or as a substitute for `other-window'. > (add-hook \\='switchy-minor-mode-hook > (lambda () > (if switchy-minor-mode > (keymap-global-set \"<remap> <other-window>\" > #\\='switchy-window) > (keymap-global-unset \"<remap> <other-window>\"))))") > > (define-minor-mode switchy-minor-mode > "Activates recording of window selection ticks. > Those are the timestamps for figuring out the last-recently-used > order of windows. > > The minor-mode provides the keymap `switchy-minor-mode-map', > which see." > :global t > :keymap switchy-minor-mode-map Isn't this the default anyway? > (if switchy-minor-mode > (add-hook 'window-selection-change-functions > #'switchy--on-window-selection-change) > (remove-hook 'window-selection-change-functions > #'switchy--on-window-selection-change))) > > (provide 'switchy) > > (provide 'switchy) Accidentally duplicated? > ;;; switchy.el ends here > -- Philip Kaludercic ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [GNU ELPA] I'd like to add switchy.el: a last-recently-used window switcher 2023-04-09 8:56 ` Philip Kaludercic @ 2023-04-09 9:33 ` Tassilo Horn 2023-04-09 10:30 ` Philip Kaludercic 2023-04-09 11:14 ` Tassilo Horn 0 siblings, 2 replies; 6+ messages in thread From: Tassilo Horn @ 2023-04-09 9:33 UTC (permalink / raw) To: Philip Kaludercic; +Cc: emacs-devel Thanks Philip, I'll address your comments before adding the package. Bye, Tassilo 09.04.2023 10:56:28 Philip Kaludercic <philipk@posteo.net>: > Tassilo Horn <tsdh@gnu.org> writes: > >> Hi all, >> >> I've written a small utility called switchy.el which I'd like to add to >> GNU ELPA. I can do that on my own but wanted to give you a chance for >> commenting first. I'm attaching the file below. >> >> In essence, the single command provided is `switchy-window' which is >> similar to `other-window' except that it switches to other windows in >> last-recently-used order instead of top-to-bottom-left-to-right order. >> With quick consecutive invocations you can reach any window but if you >> have a small delay between invocations (controlled by the single >> defcustom `switchy-delay'), you toggle between the two last recently >> used windows. >> >> Bye, >> Tassilo >> >> ;;; switchy.el --- A last-recently-used window switcher -*- lexical-binding: t; -*- >> ;; >> ;; Copyright (C) 2023 Tassilo Horn >> ;; >> ;; Author: Tassilo Horn <tsdh@gnu.org> >> ;; Version: 1.0 >> ;; Keywords: windows >> ;; Homepage: https://sr.ht/~tsdh/switchy/ >> ;; Repository: https://git.sr.ht/~tsdh/switchy >> ;; Package-Requires: ((emacs "25.1") (compat "29.1.3.4")) > > If you are to use compat 29.1.0.0 or newer then you also have to require > it! > >> ;; SPDX-License-Identifier: GPL-3.0-or-later >> ;; >> ;; This file is NOT part of GNU Emacs. > > If added to GNU ELPA, this should be change to "... is part of GNU > Emacs", right? > >> ;; >> ;; 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 <http://www.gnu.org/licenses/>. >> ;; >> ;;; Commentary: >> ;; >> ;; Switchy is a last-recently-used window switcher. It suits my personal Emacs >> ;; layout and workflow where I usually have at most two editing windows but up >> ;; to three side-windows which I have to select only seldomly. >> ;; >> ;; The idea of switchy is simple: when you invoke `switchy-window' in quick >> ;; succession, it will switch to one window after the other in >> ;; last-recently-used order. Once you stop switching for long enough time >> ;; (`switchy-delay', 1.5 seconds by default), the selected window gets locked >> ;; in, i.e., its LRU timestamp is updated and this switching sequence is ended. >> ;; Thusly, you can toggle between two windows simply by invoking >> ;; `switchy-window', waiting at least `switchy-delay', and then invoking >> ;; `switchy-window' again to switch back to the original window. >> ;; >> ;; Activate `switchy-minor-mode' which tracks window changes and bind >> ;; `switchy-window' to a key of your liking in `switchy-minor-mode-map' (or >> ;; globally, see the variable's docstring for examples). >> ;; >> ;; Hint: Since the order of window switching is not as obvious as it is with >> ;; `other-window', adding a bit visual feedback to window selection changes can >> ;; be helpful. That can be done easily with the stock Emacs pulse.el, e.g.: >> ;; >> ;; (add-hook 'window-selection-change-functions >> ;; (lambda (frame) >> ;; (when (eq frame (selected-frame)) >> ;; (pulse-momentary-highlight-one-line)))) >> >> >> ;;; Code: >> >> (defgroup switchy nil >> "Switchy is a last-recently-used window-switcher." >> :group 'windows) >> >> (defvar switchy--tick-counter 0 >> "Values of this counter represent the last-recently-used order of windows. >> Only for internal use.") >> >> (defvar switchy--tick-alist nil >> "An alist with entries (WINDOW . TICK). >> A higher TICK value means a window has more recently been visited. >> Only for internal use.") >> >> (defcustom switchy-delay 1.5 >> "Number of seconds before the current window gets locked in. >> If more time elapses between consecutive invocations of >> `switchy-window', the current window's tick (timestamp) is >> updated in `switchy--tick-alist' and the current switching cycle >> ends." >> :type 'number) >> >> (defvar switchy--timer nil >> "The timer locking in the current window after `switchy-delay' seconds. >> Only for internal use.") >> >> (defvar switchy--visited-windows nil >> "The windows having already been visited in the current switching cycle.") >> >> (defun switchy--on-window-selection-change (&optional frame) >> "Record the next `switchy--tick-counter' value for the selected window of FRAME. >> Meant to be used in `window-selection-change-functions' which is >> arranged by `switchy-minor-mode'." >> (when (eq frame (selected-frame)) >> (when switchy--timer >> (cancel-timer switchy--timer)) >> (setq switchy--timer (run-at-time >> switchy-delay nil >> (lambda () >> (setf (alist-get (selected-window) >> switchy--tick-alist) >> (cl-incf switchy--tick-counter)) >> (setq switchy--visited-windows nil)))))) >> >> (defun switchy-window (&optional arg) >> "Switch to other windows in last-recently-used order. >> If prefix ARG is given, use least-recently-used order. >> >> If the time between consecutive invocations is smaller than >> `switchy-delay' seconds, selects one after the other window in >> LRU order and cycles when all windows have been visited. If >> `switchy-delay' has passed, the current switching cycle ends and >> the now selected window gets its tick updated (a kind of >> timestamp)." >> (interactive) >> >> (unless switchy-minor-mode >> (user-error "switchy-window requires `switchy-minor-mode' being active")) >> >> ;; Remove dead windows. >> (setq switchy--tick-alist (seq-filter >> (lambda (e) >> (window-live-p (car e))) >> switchy--tick-alist)) >> ;; Add windows never selected. >> (dolist (win (window-list (selected-frame))) >> (unless (assq win switchy--tick-alist) >> (setf (alist-get win switchy--tick-alist) 0))) > > The setf is strictly speaking unnecessary here and causes an accidental > O(n^2) slowdown, since you traverse the list once to check if it has an > entry and then traverse it again to check if you can set 0 to an > existing entry. You could also just push a cons-cell to the beginning. > Then again, this is all bounded by the maximal number of windows that > someone has open so it doesn't matter in practice. > >> ;; Ensure the current window is marked as visited. >> (setq switchy--visited-windows (cons (selected-window) >> switchy--visited-windows)) >> >> (let ((win-entries (seq-filter >> (lambda (e) >> (let ((win (car e))) >> (and (eq (window-frame win) (selected-frame)) >> (or (minibuffer-window-active-p win) >> (not (eq win (minibuffer-window >> (selected-frame))))) >> (not (memq win switchy--visited-windows))))) >> switchy--tick-alist))) >> (if win-entries >> (when-let ((win (car (seq-reduce (lambda (x e) >> (if (and x (funcall (if arg #'< #'>) >> (cdr x) (cdr e))) >> x >> e)) >> win-entries nil)))) >> >> (progn > > Why the progn if you are using when-let? > >> (setq switchy--visited-windows (cons win switchy--visited-windows)) >> (select-window win))) >> ;; Start a new cycle if we're not at the start already, i.e., we visited >> ;; just one (the current) window. >> (when (> (length switchy--visited-windows) 1) > > Or (length> switchy--visited-windows 1)? > >> (setq switchy--visited-windows nil) >> (switchy-window))))) >> >> (defvar switchy-minor-mode-map (make-sparse-keymap) >> "The mode map of `switchy-minor-mode'. >> No keys are bound by default. Bind the main command >> `switchy-window' to a key of your liking, e.g., >> >> ;; That\\='s what I use. >> (keymap-set switchy-minor-mode-map \"C-<\" #\\='switchy-window) > > If you are already making use of keymap-set, you might as well define > the map itself using defvar-keymap (Compat provides it). > >> ;; Or as a substitute for `other-window'. >> (add-hook \\='switchy-minor-mode-hook >> (lambda () >> (if switchy-minor-mode >> (keymap-global-set \"<remap> <other-window>\" >> #\\='switchy-window) >> (keymap-global-unset \"<remap> <other-window>\"))))") >> >> (define-minor-mode switchy-minor-mode >> "Activates recording of window selection ticks. >> Those are the timestamps for figuring out the last-recently-used >> order of windows. >> >> The minor-mode provides the keymap `switchy-minor-mode-map', >> which see." >> :global t >> :keymap switchy-minor-mode-map > > Isn't this the default anyway? > >> (if switchy-minor-mode >> (add-hook 'window-selection-change-functions >> #'switchy--on-window-selection-change) >> (remove-hook 'window-selection-change-functions >> #'switchy--on-window-selection-change))) >> >> (provide 'switchy) >> >> (provide 'switchy) > > Accidentally duplicated? > >> ;;; switchy.el ends here >> > ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [GNU ELPA] I'd like to add switchy.el: a last-recently-used window switcher 2023-04-09 9:33 ` Tassilo Horn @ 2023-04-09 10:30 ` Philip Kaludercic 2023-04-09 11:14 ` Tassilo Horn 1 sibling, 0 replies; 6+ messages in thread From: Philip Kaludercic @ 2023-04-09 10:30 UTC (permalink / raw) To: Tassilo Horn; +Cc: emacs-devel Tassilo Horn <tsdh@gnu.org> writes: > Thanks Philip, > > I'll address your comments before adding the package. Great, looking forward to seeing the package on ELPA :) > Bye, > Tassilo > > 09.04.2023 10:56:28 Philip Kaludercic <philipk@posteo.net>: > >> Tassilo Horn <tsdh@gnu.org> writes: >> >>> Hi all, >>> >>> I've written a small utility called switchy.el which I'd like to add to >>> GNU ELPA. I can do that on my own but wanted to give you a chance for >>> commenting first. I'm attaching the file below. >>> >>> In essence, the single command provided is `switchy-window' which is >>> similar to `other-window' except that it switches to other windows in >>> last-recently-used order instead of top-to-bottom-left-to-right order. >>> With quick consecutive invocations you can reach any window but if you >>> have a small delay between invocations (controlled by the single >>> defcustom `switchy-delay'), you toggle between the two last recently >>> used windows. >>> >>> Bye, >>> Tassilo >>> >>> ;;; switchy.el --- A last-recently-used window switcher -*- lexical-binding: t; -*- >>> ;; >>> ;; Copyright (C) 2023 Tassilo Horn >>> ;; >>> ;; Author: Tassilo Horn <tsdh@gnu.org> >>> ;; Version: 1.0 >>> ;; Keywords: windows >>> ;; Homepage: https://sr.ht/~tsdh/switchy/ >>> ;; Repository: https://git.sr.ht/~tsdh/switchy >>> ;; Package-Requires: ((emacs "25.1") (compat "29.1.3.4")) >> >> If you are to use compat 29.1.0.0 or newer then you also have to require >> it! >> >>> ;; SPDX-License-Identifier: GPL-3.0-or-later >>> ;; >>> ;; This file is NOT part of GNU Emacs. >> >> If added to GNU ELPA, this should be change to "... is part of GNU >> Emacs", right? >> >>> ;; >>> ;; 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 <http://www.gnu.org/licenses/>. >>> ;; >>> ;;; Commentary: >>> ;; >>> ;; Switchy is a last-recently-used window switcher. It suits my personal Emacs >>> ;; layout and workflow where I usually have at most two editing windows but up >>> ;; to three side-windows which I have to select only seldomly. >>> ;; >>> ;; The idea of switchy is simple: when you invoke `switchy-window' in quick >>> ;; succession, it will switch to one window after the other in >>> ;; last-recently-used order. Once you stop switching for long enough time >>> ;; (`switchy-delay', 1.5 seconds by default), the selected window gets locked >>> ;; in, i.e., its LRU timestamp is updated and this switching sequence is ended. >>> ;; Thusly, you can toggle between two windows simply by invoking >>> ;; `switchy-window', waiting at least `switchy-delay', and then invoking >>> ;; `switchy-window' again to switch back to the original window. >>> ;; >>> ;; Activate `switchy-minor-mode' which tracks window changes and bind >>> ;; `switchy-window' to a key of your liking in `switchy-minor-mode-map' (or >>> ;; globally, see the variable's docstring for examples). >>> ;; >>> ;; Hint: Since the order of window switching is not as obvious as it is with >>> ;; `other-window', adding a bit visual feedback to window selection changes can >>> ;; be helpful. That can be done easily with the stock Emacs pulse.el, e.g.: >>> ;; >>> ;; (add-hook 'window-selection-change-functions >>> ;; (lambda (frame) >>> ;; (when (eq frame (selected-frame)) >>> ;; (pulse-momentary-highlight-one-line)))) >>> >>> >>> ;;; Code: >>> >>> (defgroup switchy nil >>> "Switchy is a last-recently-used window-switcher." >>> :group 'windows) >>> >>> (defvar switchy--tick-counter 0 >>> "Values of this counter represent the last-recently-used order of windows. >>> Only for internal use.") >>> >>> (defvar switchy--tick-alist nil >>> "An alist with entries (WINDOW . TICK). >>> A higher TICK value means a window has more recently been visited. >>> Only for internal use.") >>> >>> (defcustom switchy-delay 1.5 >>> "Number of seconds before the current window gets locked in. >>> If more time elapses between consecutive invocations of >>> `switchy-window', the current window's tick (timestamp) is >>> updated in `switchy--tick-alist' and the current switching cycle >>> ends." >>> :type 'number) >>> >>> (defvar switchy--timer nil >>> "The timer locking in the current window after `switchy-delay' seconds. >>> Only for internal use.") >>> >>> (defvar switchy--visited-windows nil >>> "The windows having already been visited in the current switching cycle.") >>> >>> (defun switchy--on-window-selection-change (&optional frame) >>> "Record the next `switchy--tick-counter' value for the selected window of FRAME. >>> Meant to be used in `window-selection-change-functions' which is >>> arranged by `switchy-minor-mode'." >>> (when (eq frame (selected-frame)) >>> (when switchy--timer >>> (cancel-timer switchy--timer)) >>> (setq switchy--timer (run-at-time >>> switchy-delay nil >>> (lambda () >>> (setf (alist-get (selected-window) >>> switchy--tick-alist) >>> (cl-incf switchy--tick-counter)) >>> (setq switchy--visited-windows nil)))))) >>> >>> (defun switchy-window (&optional arg) >>> "Switch to other windows in last-recently-used order. >>> If prefix ARG is given, use least-recently-used order. >>> >>> If the time between consecutive invocations is smaller than >>> `switchy-delay' seconds, selects one after the other window in >>> LRU order and cycles when all windows have been visited. If >>> `switchy-delay' has passed, the current switching cycle ends and >>> the now selected window gets its tick updated (a kind of >>> timestamp)." >>> (interactive) >>> >>> (unless switchy-minor-mode >>> (user-error "switchy-window requires `switchy-minor-mode' being active")) >>> >>> ;; Remove dead windows. >>> (setq switchy--tick-alist (seq-filter >>> (lambda (e) >>> (window-live-p (car e))) >>> switchy--tick-alist)) >>> ;; Add windows never selected. >>> (dolist (win (window-list (selected-frame))) >>> (unless (assq win switchy--tick-alist) >>> (setf (alist-get win switchy--tick-alist) 0))) >> >> The setf is strictly speaking unnecessary here and causes an accidental >> O(n^2) slowdown, since you traverse the list once to check if it has an >> entry and then traverse it again to check if you can set 0 to an >> existing entry. You could also just push a cons-cell to the beginning. >> Then again, this is all bounded by the maximal number of windows that >> someone has open so it doesn't matter in practice. >> >>> ;; Ensure the current window is marked as visited. >>> (setq switchy--visited-windows (cons (selected-window) >>> switchy--visited-windows)) >>> >>> (let ((win-entries (seq-filter >>> (lambda (e) >>> (let ((win (car e))) >>> (and (eq (window-frame win) (selected-frame)) >>> (or (minibuffer-window-active-p win) >>> (not (eq win (minibuffer-window >>> (selected-frame))))) >>> (not (memq win switchy--visited-windows))))) >>> switchy--tick-alist))) >>> (if win-entries >>> (when-let ((win (car (seq-reduce (lambda (x e) >>> (if (and x (funcall (if arg #'< #'>) >>> (cdr x) (cdr e))) >>> x >>> e)) >>> win-entries nil)))) >>> >>> (progn >> >> Why the progn if you are using when-let? >> >>> (setq switchy--visited-windows (cons win switchy--visited-windows)) >>> (select-window win))) >>> ;; Start a new cycle if we're not at the start already, i.e., we visited >>> ;; just one (the current) window. >>> (when (> (length switchy--visited-windows) 1) >> >> Or (length> switchy--visited-windows 1)? >> >>> (setq switchy--visited-windows nil) >>> (switchy-window))))) >>> >>> (defvar switchy-minor-mode-map (make-sparse-keymap) >>> "The mode map of `switchy-minor-mode'. >>> No keys are bound by default. Bind the main command >>> `switchy-window' to a key of your liking, e.g., >>> >>> ;; That\\='s what I use. >>> (keymap-set switchy-minor-mode-map \"C-<\" #\\='switchy-window) >> >> If you are already making use of keymap-set, you might as well define >> the map itself using defvar-keymap (Compat provides it). >> >>> ;; Or as a substitute for `other-window'. >>> (add-hook \\='switchy-minor-mode-hook >>> (lambda () >>> (if switchy-minor-mode >>> (keymap-global-set \"<remap> <other-window>\" >>> #\\='switchy-window) >>> (keymap-global-unset \"<remap> <other-window>\"))))") >>> >>> (define-minor-mode switchy-minor-mode >>> "Activates recording of window selection ticks. >>> Those are the timestamps for figuring out the last-recently-used >>> order of windows. >>> >>> The minor-mode provides the keymap `switchy-minor-mode-map', >>> which see." >>> :global t >>> :keymap switchy-minor-mode-map >> >> Isn't this the default anyway? >> >>> (if switchy-minor-mode >>> (add-hook 'window-selection-change-functions >>> #'switchy--on-window-selection-change) >>> (remove-hook 'window-selection-change-functions >>> #'switchy--on-window-selection-change))) >>> >>> (provide 'switchy) >>> >>> (provide 'switchy) >> >> Accidentally duplicated? >> >>> ;;; switchy.el ends here >>> >> -- Philip Kaludercic ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [GNU ELPA] I'd like to add switchy.el: a last-recently-used window switcher 2023-04-09 9:33 ` Tassilo Horn 2023-04-09 10:30 ` Philip Kaludercic @ 2023-04-09 11:14 ` Tassilo Horn 1 sibling, 0 replies; 6+ messages in thread From: Tassilo Horn @ 2023-04-09 11:14 UTC (permalink / raw) Cc: Philip Kaludercic, emacs-devel Tassilo Horn <tsdh@gnu.org> writes: Hi Philip, I've added the package to ELPA as switchy-window as suggested by Eli. >>> ;; Package-Requires: ((emacs "25.1") (compat "29.1.3.4")) >> >> If you are to use compat 29.1.0.0 or newer then you also have to >> require it! Ah, thanks. I'm new to the compat game but it's a relief not to worry about compatibility. :-) >>> ;; SPDX-License-Identifier: GPL-3.0-or-later >>> ;; >>> ;; This file is NOT part of GNU Emacs. >> >> If added to GNU ELPA, this should be change to "... is part of GNU >> Emacs", right? Yes, I've changed that now. >>> ;; Remove dead windows. >>> (setq switchy--tick-alist (seq-filter >>> (lambda (e) >>> (window-live-p (car e))) >>> switchy--tick-alist)) >>> ;; Add windows never selected. >>> (dolist (win (window-list (selected-frame))) >>> (unless (assq win switchy--tick-alist) >>> (setf (alist-get win switchy--tick-alist) 0))) >> >> The setf is strictly speaking unnecessary here and causes an >> accidental O(n^2) slowdown, since you traverse the list once to check >> if it has an entry and then traverse it again to check if you can set >> 0 to an existing entry. You could also just push a cons-cell to the >> beginning. Then again, this is all bounded by the maximal number of >> windows that someone has open so it doesn't matter in practice. Yup, I've kept that as-is because the theoretical slowdown is not practical and it's easier to debug when the alist ist free of duplicates. >>> (let ((win-entries (seq-filter >>> (lambda (e) >>> (let ((win (car e))) >>> (and (eq (window-frame win) (selected-frame)) >>> (or (minibuffer-window-active-p win) >>> (not (eq win (minibuffer-window >>> (selected-frame))))) >>> (not (memq win switchy--visited-windows))))) >>> switchy--tick-alist))) >>> (if win-entries >>> (when-let ((win (car (seq-reduce (lambda (x e) >>> (if (and x (funcall (if arg #'< >>> #'>) >>> (cdr x) (cdr >>> e))) >>> x >>> e)) >>> win-entries nil)))) >>> >>> (progn >> >> Why the progn if you are using when-let? A left-over of an earlier version. >>> ;; Start a new cycle if we're not at the start already, i.e., we >>> visited >>> ;; just one (the current) window. >>> (when (> (length switchy--visited-windows) 1) >> >> Or (length> switchy--visited-windows 1)? Oh, I didn't know that. >>> (defvar switchy-minor-mode-map (make-sparse-keymap) >>> "The mode map of `switchy-minor-mode'. >>> No keys are bound by default. Bind the main command >>> `switchy-window' to a key of your liking, e.g., >>> >>> ;; That\\='s what I use. >>> (keymap-set switchy-minor-mode-map \"C-<\" #\\='switchy-window) >> >> If you are already making use of keymap-set, you might as well define >> the map itself using defvar-keymap (Compat provides it). Yes, thanks! >>> (define-minor-mode switchy-minor-mode >>> "Activates recording of window selection ticks. >>> Those are the timestamps for figuring out the last-recently-used >>> order of windows. >>> >>> The minor-mode provides the keymap `switchy-minor-mode-map', >>> which see." >>> :global t >>> :keymap switchy-minor-mode-map >> >> Isn't this the default anyway? Yes, it is. Removed. >>> (provide 'switchy) >>> >>> (provide 'switchy) >> >> Accidentally duplicated? Just to be on the safe side it is really provided! ;-) Thanks, Tassilo ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2023-04-09 11:14 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2023-04-09 7:36 [GNU ELPA] I'd like to add switchy.el: a last-recently-used window switcher Tassilo Horn 2023-04-09 8:08 ` Eli Zaretskii 2023-04-09 8:56 ` Philip Kaludercic 2023-04-09 9:33 ` Tassilo Horn 2023-04-09 10:30 ` Philip Kaludercic 2023-04-09 11:14 ` Tassilo Horn
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/emacs.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).