From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Pranshu Sharma Newsgroups: gmane.emacs.devel Subject: Re: Add function to rotate/transpose all windows Date: Sun, 01 Dec 2024 16:41:22 +1000 Message-ID: <87zflfc3cd.fsf@gmail.com> References: <87setpdv21.fsf@gmail.com> <87cyiuefxs.fsf@gmail.com> <878qthewbq.fsf@gmail.com> <8599bc67-b05d-4afc-8e6e-1ba64a30054e@gmx.at> <87frnp2x85.fsf@gmail.com> <823c7cca-63d4-4568-94bc-11f5949d6c5c@gmx.at> <87h683muss.fsf@gmail.com> <02432e6c-6ee2-4c68-9ebb-246f6be88918@gmx.at> <877c8wadke.fsf@gmail.com> <878qt8spp2.fsf@gmail.com> <0ce35c7a-8b28-4905-a6ab-caf50f2fc750@gmx.at> <87mshl2i6h.fsf@gmail.com> <9b460366-f34e-48f6-a680-e7fa5bc7f598@gmx.at> <87bjy03fql.fsf@gmail.com> <356d63bc-818c-428c-b31b-a0eb227b3a8a@gmx.at> <87o720gjst.fsf@gmail.com> <7dfe87a0-b367-47df-86df-f8fd95163fd6@gmx.at> <8734jbz8sb.fsf@gmail.com> <50ab6c0a-6afb-4727-9094-178668fc4f4e@gmx.at> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="36269"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Cc: Juri Linkov , Eli Zaretskii , emacs-devel@gnu.org To: martin rudalics Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sun Dec 01 08:57:08 2024 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1tHepI-0009IP-B4 for ged-emacs-devel@m.gmane-mx.org; Sun, 01 Dec 2024 08:57:08 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tHeob-0006D6-FJ; Sun, 01 Dec 2024 02:56:25 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tHde8-0000dc-PD for emacs-devel@gnu.org; Sun, 01 Dec 2024 01:41:33 -0500 Original-Received: from mail-pl1-x62f.google.com ([2607:f8b0:4864:20::62f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1tHde6-000288-Oo; Sun, 01 Dec 2024 01:41:32 -0500 Original-Received: by mail-pl1-x62f.google.com with SMTP id d9443c01a7336-21271dc4084so23435275ad.2; Sat, 30 Nov 2024 22:41:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1733035287; x=1733640087; darn=gnu.org; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:from:to:cc:subject:date:message-id:reply-to; bh=xcHFUI+fXpXzgZxBxfrm65r+Rdt/PdwFHkdPjElEwEs=; b=hIK6xE9S7L8ji61nxEgCHxCWw8C3EdhOMeRdKqU0G3hNVE6gqA6erRZI7gkLBoITG9 A8UVZD130b65ETLGEN9ri8BZGA3vIsNIwLMB4La5Alta5OzsMqQK2IrOPo7xR2U8d8Ue XYRdR4ozraXTa6JH/5QMczzh9YlfSTmsRp8EGUzDW31M+ez108mhb1ackMMzEvGrHgOz eQLuxYsdW6T+Tchyt03RCdOfUD+9FoquwoTq2ZOkYcdSvZn/Dr0GNFu5L6DVwnoEIy6Z NLeP/m6G275dyOTCLCezeTRcw/4icL+S5WKr9e59UXEY7zHbKG1psRwWWUgsUSzpI7vC yK2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1733035287; x=1733640087; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=xcHFUI+fXpXzgZxBxfrm65r+Rdt/PdwFHkdPjElEwEs=; b=P4Vt1ud/jLY//LC36zpjmkJ53nm7l6nelICcWwgF7+fH1GHWNGNONps41QC5qy2foN 1VItc2/oljY6/5jYkyoY4gSUdR+wZwLnE6ws2tp/sxQ9HJPJYnOpmsjYOjg3w8Zz0J4/ QDT8Alx3hQNmLiqhS3UiAd6iqcTY7F7l7dTK1r7rt0LFOyEd+Nm6+m6wyKo/ugO3EISz dSgQDRhoAk2JVX2ZMgXGIUr946MbueSbcOufg0Cf8HtZI1bdzAqU7G22GmLk5gYTsqsG 475PKOeuhuxuCSpNyRPWnn5ntJzufw9DPC1lWCVRjH38nD93GQLBeBNBVb6CVrS7Sy6X cUPg== X-Forwarded-Encrypted: i=1; AJvYcCUazvLmbty/7m/zuuMtdlwKCr7Xgwt8ZHwLplEGOhYE9oQ56HCtoKen2xqJZvtayU1zGZxD@gnu.org, AJvYcCXfnLd50UXn6npPn1mEhymy0lFZhAkiGpvQZd6+oalYgweT3DNl4t5wlkS+6SAZ+RYlaM0fuir6TTij4KQ=@gnu.org X-Gm-Message-State: AOJu0YyXc+H+6AOL3PjgeElV4mEE+6zJJItX3GbbkqrQZjmxyUYFm6PL ax3p96y6pzBjFyUhQFxiSiDKyNurP5m3lRRxwPFL4Jgk422FMp6vLd9G3UDD X-Gm-Gg: ASbGncv9bduaCKnZmDbxKv/NPdPuA3e9JJC/RXCYo+GIDB3TDF++7mdyCfcVQsqR6a3 IvHiaIRaznx2uKD7xJGyx58E0PKnP8scxhRlBei64s8IL8NOKKSpQr2599Jk4H+CHWJzwZkLwSE bxc7b0CSI81m77d3Khnil8GHkhN7F0jBqDbLmFDnSfj+lawErsKlweyXRhXwvOkmzoypezd2MUk DHrzaRHkSHDl8Q9p+SD74wnsJYP6qGHbCfB/Rr7LcZCoCY7Pnw77Q== X-Google-Smtp-Source: AGHT+IFXOyFzPAcCt9jSa9zCXnzmeVyDhFdZET5OIpQP+d4skhp34FiAOQ0EJnzacAx9WK+iEXJz0A== X-Received: by 2002:a17:902:d50d:b0:212:10ff:4b80 with SMTP id d9443c01a7336-21501e6971fmr201830575ad.54.1733035286840; Sat, 30 Nov 2024 22:41:26 -0800 (PST) Original-Received: from pebl ([2001:8003:7816:8300:ace6:d955:2ded:457c]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-215219683e5sm54821845ad.145.2024.11.30.22.41.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 30 Nov 2024 22:41:26 -0800 (PST) In-Reply-To: <50ab6c0a-6afb-4727-9094-178668fc4f4e@gmx.at> (martin rudalics's message of "Sat, 30 Nov 2024 11:07:41 +0100") Received-SPF: pass client-ip=2607:f8b0:4864:20::62f; envelope-from=pranshusharma366@gmail.com; helo=mail-pl1-x62f.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Sun, 01 Dec 2024 02:56:20 -0500 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:325906 Archived-At: --=-=-= Content-Type: text/plain martin rudalics writes: >> Right now, I feel the code is in a pretty complete state. > > Let's try to polish it up a little so it becomes easier to read. > > The doc-string of 'window-tree-pixel-sizes' should be updated. Maybe it > should be renamed to 'window-tree-normal-sizes' too. Also, the repeated > consing somehow obscures the fact that you are constructing a list for > each window. So I would write, instead of > > ((window-top-child window) > (cons (cons t window) (cons (cons (window-normal-size window nil) > (window-normal-size window t)) > (window-tree-pixel-sizes > (window-top-child window) t)))) > > something like > > ((window-top-child window) > (list t window > (window-normal-size window nil) > (window-normal-size window t) > (window-tree-pixel-sizes (window-top-child window) t))) > > You obviously would have to make sure that my interpretation of your > code is correct. The problem with this is that the reutn would be: (nil/t win height width (w1 w2 w3)) instead of: (nil/t win height width w1 w2 w3) So I slightly modified your code > In the commands I wonder whether the SUBTREE argument is necessary. > Couldn't we simply say that FRAME-OR-WINDOW defaults to the parent of > the selected window if it exists and to the selected frame's root window > otherwise. Ok, I did something similar, the problem with your idea was that if it default to parent of selected window, then it wouldn't transpose the frame by defualt, user will transpose frame much more than transpose a subsection. The change I made makes it so that is works as before, but without subtree and with the extra functionaility of being able to pass a numric arg which will determine how {opposite word of deep here} of windows to transpose. > > 'cycle-windows' is still a mystery to me. I think we must first make a > clear distinction between windows and the buffers they display here. So > far there was no need for that since "window A" meant just the window > showing buffer A or the parent window A. That is, we silently assumed > some sort of identity mapping between window and buffer names. The problem with this is that that the buffers of the windows are not changed, rather only the windows, so this would be technically inaccurate. > > CONF, DO-NOT-CONVERT-SIZE and ATOM-WINDOWS should IMHO become lexically > bound variables. IIUC they are never altered within the body of > 'window--transpose-1' and only clutter up the recursions. I 100% agree, but I don't know how to do this without dynamic scoping. I tried `dlet', but that didn't work. Only way I can think of is nested functions, which idk if you would want me to do. > 'fwin' should become 'first-window' since "f" alone could be taken for > "frame". And instead of 'selwin' I'd write 'selected-window'. Also > writing 'win' instead of 'window' is hardly ever a "win" in my > experience. Ok, I ended the win streak. > (mapc 'window-make-atom atom-windows) > > could be a bit of a problem (it assumes that 'window--atom-check' has > done its work in between) but let's leave it alone for the moment. split-window calls window--check which calls window--atom-check, so I think it's fair to assume window--atom-check has been called. And if it hasn't then it shouldn't change anything iiuc. > I think these have become obsolete now. I kept the flen one as it's still in use. > is a very uncommon thing to do. Can't you use a lambda here? Turns out it was uncessasary, before I needed it for something so I could set it's value before bidning aditional stuff, not I just moved it to the start of the BODY block of the let. > The words that for me fit these meanings are > transpose-window-layout and rotate-window-layout. Ok, done I changed the names. But so far I can't come up with a docsting that will also be tehcnicaly correct(can't do the buffer change windows same route), and also understandable. I think the best way would be to explain visually, as Juri suggested. Is it fine to include ascii art in docstrings? --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=window-transpose.el Content-Transfer-Encoding: quoted-printable ;;; -*- lexical-binding:t -*- (defun window-tree-normal-sizes (window &optional next) "Return normal sizes of all windows rooted at WINDOW. A list of the form (SPLIT-TYPE PARENT-WIN PARENT-WIN-HEIGHT PARENT-WIN-WIDTH W1 W2 ...) is returned. SPLIT-TYPE is non-nil if PARENT-WIN is split horizontally. PARENT-WIN is the internal window. PARENT-WIN-HEIGHT and PARENT-WIN-WIDTH are the normal heights of PARENT-WIN. Wn is a list of the form (WINDOW HEIGHT WIDTH) where HEIGHT and WIDTH are the normal height and width of the window." (let (list) (while window (setq list (cons (cond ((window-top-child window) `(t ,window ,(window-normal-size window nil) ,(window-normal-size window t) . ,(window-tree-normal-sizes (window-top-child window) t))) ((window-left-child window) `(nil ,window ,(window-normal-size window nil) ,(window-normal-size window t) . ,(window-tree-normal-sizes (window-left-child window) t))) (t (list window (window-normal-size window nil) (window-normal-size window t)))) list)) (setq window (when next (window-next-sibling window)))) (nreverse list))) (defun window--window-to-transpose(frame-or-window) "Return the window to be acted upon by `window--transpose'. If FRAME-OR-WINDOW is a window return FRAME-OR-WINDOW. If FRAME-OR-WINDOW is a frame, return FRAME-OR-WINDOW's main window. If FRAME-OR-WINDOW is nil, than the frames main window wil be returned. If frame-or-window is a number, return ancestor FRAME-OR-WINDOWth ancestor of the selected window. If FRAME-OR-WINDOW is non-nil, and not a frame or a window or a number, than the return value will be the parent window of the selected window." (cond ((numberp frame-or-window) (let ((window (selected-window)) (top (window-main-window))) (dotimes (_ frame-or-window) (unless (eq window top) (setq window (window-parent window)))) window)) ((windowp frame-or-window) frame-or-window) ((or (framep frame-or-window) (not frame-or-window)) (window-main-window frame-or-window)) (frame-or-window (window-parent)))) (defun rotate-windows-anticlockwise (&optional frame-or-window) "Rotate windows of FRAME-OR-WINDOW anticlockwise by 90 degrees. The window to be transposed will be found by applying `window--window-to-transpose' to FRAME-OR-WINDOW, refer to that function to see how FRAME-OR-WINDOW will be handled." (interactive "P") (let ((window (window--window-to-transpose frame-or-window))) (window--transpose window '(right . above) nil))) (defun rotate-windows-clockwise (&optional frame-or-window) "Rotate windows of FRAME-OR-WINDOW clockwise by 90 degrees. The window to be transposed will be found by applying `window--window-to-transpose' to FRAME-OR-WINDOW, refer to that function to see how FRAME-OR-WINDOW will be handled." (interactive "P") (let ((window (window--window-to-transpose frame-or-window))) (window--transpose window '(left . below) nil))) (defun flip-windows-horizontally (&optional frame-or-window) "Horizontally flip windows of FRAME-OR-WINDOW. The window to be transposed will be found by applying `window--window-to-transpose' to FRAME-OR-WINDOW, refer to that function to see how FRAME-OR-WINDOW will be handled."" If FRAME-OR-WINDOW is non-ni= l, and not a frame or a window, than FRAME-OR-WINDOW will be set to the parent window of the selected window." (interactive "P") (let ((window (w frame-or-window subtree))) (window--transpose window '(below . left) t))) (defun flip-windows-vertically (&optional frame-or-window) "Horizontally flip windows of FRAME-OR-WINDOW. The window to be transposed will be found by applying `window--window-to-transpose' to FRAME-OR-WINDOW, refer to that function to see how FRAME-OR-WINDOW will be handled." (interactive "P") (let ((window (wind frame-or-window subtree))) (window--transpose window '(above . right) t))) (defun transpose-windows (&optional frame-or-window) "Transpose windows of FRAME-OR-WINDOW. The window to be transposed will be found by applying `window--window-to-transpose' to FRAME-OR-WINDOW, refer to that function to see how FRAME-OR-WINDOW will be handled." (interactive "P") (let ((window (window--window-to-transpose frame-or-window))) (window--transpose window '(right . below) nil))) (defun window--depmap(fun ls) "Map FUN across all nodes of LS." (if (consp ls) (cons (if (consp (car ls)) (window--depmap fun (car ls)) (funcall fun (car ls))) (window--depmap fun (cdr ls))) (funcall fun ls))) (defun rotate-window-layout-reverse(&optional frame-or-window) (interactive "P") (rotate-window-layout subtree frame-or-window t)) (defun rotate-window-layout (&optional frame-or-window reverse) "This cycles windows basiclly so that for all windows, each window becomes it's `other-window'. +-----+ +-----+ | A | | C | |-----| -> |-----| |B | C| |A | B| +-----+ +-----+ " (interactive "P") (let ((window (window--window-to-transpose frame-or-window))) (if (or (not window) (window-live-p window)) (message "No windows to transpose") (let* ((frame (window-frame window)) (selected-window (frame-selected-window window)) (win-tree (car (window-tree-normal-sizes window))) (winls (seq-filter 'window-live-p (flatten-list win-tree))) (rotated-ls (if reverse (append (cdr winls) (list (car winls))) (append (last winls) winls))) (other-window-arg (if reverse 1 -1)) (first-window (car rotated-ls)) (new-win-tree (window--depmap (lambda (x) (if (window-live-p x) (pop rotated-ls) x)) win-tree))) (if (or (seq-some 'window-atom-root winls) (seq-some 'window-fixed-size-p winls)) (message "This does not work with fixed size or atom windows.") (progn ;; All child windows need to be recursively deleted. (delete-other-windows-internal first-window window) ;; (delete-dups atom-windows) (window--transpose-1 new-win-tree first-window '(below . right) t ni= l) (set-frame-selected-window frame selected-window) (other-window other-window-arg) (while (not (memq (selected-window) winls)) (other-window other-window-arg)))))))) (defun window--transpose (window conf do-not-convert-size) "Rearrange windows of WINDOW recursively. CONF should be a cons cell: (HORIZONTAL-SPLIT . VERTICAL-SPLIT) where HORIZONTAL-SPLIT will be used as the third argument of `split-window' when splitting a window that was previously horizontally split, and VERTICAL-SPLIT as third argument of `split-window' for a window that was previously vertically split. If DO-NOT-CONVERT-SIZE non-nil, the size argument of the window-split is converted from vertical to horizontal or vice versa, with the same proportion of the total split." (if (or (not window) (window-live-p window)) (message "No windows to transpose") (let* ((frame (window-frame window)) (first-window window) (selected-window (frame-selected-window window)) (win-tree (car (window-tree-normal-sizes window))) (win-list (seq-filter 'window-live-p (flatten-list win-tree))) (atom-windows (remq nil (mapcar 'window-atom-root win-list)))) (if (and (not (eq (car atom-windows) window)) (or do-not-convert-size (and (not atom-windows) (not (seq-some 'window-fixed-size-p win-list))))) (progn (delete-dups atom-windows) (while (not (window-live-p first-window)) (setq first-window (window-child first-window))) (delete-other-windows-internal first-window window) (window--transpose-1 win-tree first-window conf do-not-convert-size at= om-windows) ;; Go back to previously selected window. (set-frame-selected-window frame selected-window) (mapc 'window-make-atom atom-windows)) (message "This does not work with fixed size or atom windows."))))) (defun window--transpose-1 (subtree cwin conf do-not-convert-size atom-wind= ows) "Subroutine of `window--transpose'. SUBTREE must be in the format of the result of `window-tree-normal-sizes'. CWIN is the current window through which the window splits are made. ATOM-WINDOWS is a list of internal atom windows. The CONF and DO-NOT-CONVERT-SIZE arguments are the same as the ones in `window--transpose'." ;; `flen' is max size the window could be converted to the opposite ;; of the given split type. (let ((parent-window-is-set t) (flen (if (funcall (if do-not-convert-size 'not 'identity) (car subtree)) (float (window-pixel-width cwin)) (float (window-pixel-height cwin))))) (mapc (pcase-lambda (`(,window . ,size)) (prog1 (let* ((split-size (- (round (* flen size)))) (split-type (funcall (if (car subtree) 'car 'cdr) conf)) (return-win (if (listp window) ;; `window' is a window subtree. ;; `first-child' is a live window that is an descended of window (let* ((first-child window) ;; If the window being split is atomic (is-atom ;; cadr will return the internal parent window (memq (cadr first-child) atom-windows))) ;; (caar (cddddr first-child)) is the first window in the ;; list if there is a live window. (while (not (windowp (caar (cddddr first-child)))) (setq first-child (car (cddddr first-child)))) (window--transpose-1 window (let ((window-combination-limit parent-window-is-set)) (split-window cwin split-size split-type t (if window-combination-limit (cons (caar (cddddr first-child)) (cadr subtree)) (caar (cddddr first-child))))) (if is-atom '(nil . t) conf) do-not-convert-size atom-windows)) ;; `window' is a window. (split-window cwin split-size split-type t ;; We need to set parent window if it hasn't been set ;; already. (if parent-window-is-set (cons window (cadr subtree)) window))))) (when (eq window-combination-limit t) (set-window-combination-limit (cadr subtree) nil)) return-win) (setq parent-window-is-set nil))) (mapcar (lambda (e) (pcase-let* ((`(,window . ,window-size-info) (if (windowp (car e)) (cons (car e) e) (cons e (cdr e))))) (cons window ;; The respective size of the window. (if (car subtree) (cadr window-size-info) (caddr window-size-info))))) ;; We need to ingore first 5 elements of window list, we ignore ;; window split type, sizes and the first window (it's ;; implicitly created). We just have a list of windows. (nreverse (cdr (cddddr subtree))))) ;; (caar (cddddr subtree)) is the first child window of subtree. (unless (windowp (caar (cddddr subtree))) (let ((is-atom (memq (cadr (cadr (cddddr subtree))) atom-windows))) (window--transpose-1 (car (cddddr subtree)) cwin (if is-atom '(nil . t) co= nf) do-not-convert-size atom-windows))))) (define-key global-map (kbd "C-x w t") 'transpose-windows) (define-key global-map (kbd "C-x w c") 'rotate-window-layout) (define-key global-map (kbd "C-x w r") 'rotate-windows-clockwise) (define-key global-map (kbd "C-x w f") 'flip-windows-vertically) (define-key global-map (kbd "C-x w h") 'flip-windows-horizontally) --=-=-=--