branch: externals/brief commit 1d1d8fe291d785512eaa746dc2e5cd6dac96585c Author: Luke Lee <luke.yx....@gmail.com> Commit: Luke Lee <luke.yx....@gmail.com>
* brief.el: Window merge optionally respect atomic window settings. Compatibility fix, clean up and clarification of comments. (brief-merge-window-respect-atomicity): new customization setting. (brief-window-atomic-p, brief-force-delete-window, brief-win-add-winfo, brief-atomic-window-group, brief-window-list, brief-win-search): New functions to support window atomicity. (brief-first-common-ancestor, brief-traverse-layout, brief-window-layout, brief-merge-neighbor-window): Enhancements for respecting window atomicity, also attempt to restore as many window states as possible upon window layout reconstruction. (brief-tolerant, brief=, brief-win-left, brief-win-right, brief-win-top, brief-win-bottom, brief-merge-win, brief-split-merge, brief-delete-window-up, brief-delete-window-down, brief-delete-window-left, brief-delete-window-right): use `cl-first' instead of `first', as well as `second' and other compatibility fix. * README.org: minor documentation updates. --- README.org | 24 ++-- brief.el | 382 +++++++++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 293 insertions(+), 113 deletions(-) diff --git a/README.org b/README.org index eec1e19916..056204b72c 100644 --- a/README.org +++ b/README.org @@ -83,7 +83,7 @@ this Brief Mode. |=============================================|=====================| | *Command & 'K'eyword* | *'K'ey* | |=============================================|=====================| - | e'X'it Emacs Brief Emulator: | =<Alt>-X= | + | e'X'it Emacs Brief Mode/Emulator: | =<Alt>-X= | |---------------------------------------------+---------------------| | 'E'dit a file: | =<Alt>-E= | |---------------------------------------------+---------------------| @@ -107,10 +107,10 @@ this Brief Mode. | 'G'oto line: | =<Alt>-G= | |---------------------------------------------+---------------------| | 'L'ine oriented text selecting: | =<Alt>-L= | - | followed by cursor commands | | + | followed by cursor movements | | |---------------------------------------------+---------------------| | 'C'olumn oriented text selecting: | =<Alt>-C= | - | followed by cursor commands | | + | followed by cursor movements | | |---------------------------------------------+---------------------| | Start line 'M'arking: | =<Alt>-M= | |---------------------------------------------+---------------------| @@ -120,13 +120,13 @@ this Brief Mode. | Cut ('-') text selection into clipboard: | =<Keypad ->= | | if no text selected, cut current line | =<Shift>-<Delete>= | |---------------------------------------------+---------------------| - | Paste clipboard texts into current line: | =<Insert>= | - | if text selected, replace selected | | + | Paste/insert clipboard texts into current | =<Insert>= | + | line, if text selected, replace selected: | | |---------------------------------------------+---------------------| | 'D'elete a line(s): | =<Alt>-D= | | if text selected, delete selected | | |---------------------------------------------+---------------------| - | 'K'ill till end of line: | =<Alt>-K= | + | 'K'ill texts till end of line: | =<Alt>-K= | |---------------------------------------------+---------------------| | 'R'ead a file and insert into current line: | =<Alt>-R= | |---------------------------------------------+---------------------| @@ -229,13 +229,13 @@ this Brief Mode. |----------------------------------------------------+--------------| | Switch to the window the arrow points to | =<F1> <arrow>= | |----------------------------------------------------+--------------| - | Adjust current window size on the border side the | =<F2> <arrow>= | - | arrow points to | | + | Adjust current window edge on the side the arrow | =<F2> <arrow>= | + | points to | | |----------------------------------------------------+--------------| | Split a new window in the direction that the arrow | =<F3> <arrow>= | | points to | | |----------------------------------------------------+--------------| - | Delete the window the arrow points to | =<F4> <arrow>= | + | Merge and delete the window the arrow points to | =<F4> <arrow>= | |----------------------------------------------------+--------------| | Delete current window | =<Ctrl>-<F4>= | |----------------------------------------------------+--------------| @@ -250,7 +250,7 @@ this Brief Mode. | Close current window frame. This is usually controlled | =<Alt>-<F4>= | | by system window manager; if user accidentally closed | | | a frame use =<C-u> <Alt>-<F3>= to restore it) | | - |--------------------------------------------------------+------------ | + |--------------------------------------------------------+------------| ** Keystroke macro commands: @@ -259,7 +259,7 @@ this Brief Mode. |--------------------------------------------------------+--------------| | Pause recording keystroke macro | =<Shift>-<F7>= | |--------------------------------------------------------+--------------| - | Playback just recorded macro | =<F8>= | + | Playback the just recorded macro | =<F8>= | |--------------------------------------------------------+--------------| | Load keystroke macro from a file, will prompt for a | =<Alt>-<F7>= | | file name | | @@ -281,7 +281,7 @@ this Brief Mode. | compilation buffer | | |----------------------------------------------------------+-------------| -* _Emacs specific or miscellaneous extended commands:_ +* _Emacs specific and miscellaneous extended commands:_ |---------------------------------------------------+------------------------| | Move backwards an expression, or matching | =<Alt>-<Left>= | diff --git a/brief.el b/brief.el index 337410ce66..7021f02235 100644 --- a/brief.el +++ b/brief.el @@ -5,7 +5,7 @@ ;; Author: Luke Lee <luke.yx....@gmail.com> ;; Maintainer: Luke Lee <luke.yx....@gmail.com> ;; Keywords: brief, emulations, crisp -;; Version: 5.90 +;; Version: 5.91 ;; Package-Requires: ((nadvice "0.3") (cl-lib "0.5")) ;; GNU Emacs is free software: you can redistribute it and/or modify @@ -95,7 +95,7 @@ ;; CRiSP from Emacs source repository to ELPA. ;; ;; In early 2024, the Brief-style window merge on deletion (the [F4] -;; [<arrows>] commands) is finally supported, a task I had postponed +;; [<arrows>] commands) are finally supported, a task I had postponed ;; for over 20 years due to not finding time to implement it. ;; @@ -462,7 +462,7 @@ ;; backward compatibility issues. ;;(require 'replace) -(defconst brief-version "5.90" +(defconst brief-version "5.91" "Current version of this Brief editor mode/emulator.") ;; @@ -1273,6 +1273,18 @@ This option is effective only if `brief-merge-deleted-window' is non-NIL. Also see its description for layouts that are not supported." :type 'boolean) +(defcustom brief-merge-window-respect-atomicity nil + "Respect Emacs atomic window settings when attempted to merge a window. +Emacs atomic windows are treated as a atomic group that can't be deleted +separately, therefore any window merge attempt on an atomic window is +not allowed. When this flag is set to NIL (default) Brief is allow to +break their atomicity. Also notice that when this flag is set to +non-NIL, the probability of window merge failure might become higher as +Brief need to keep the atomic windows intact and hence more likely to +fail on finding a suitable new layout for the merging operation. This +option is effective only if `brief-merge-deleted-window' is non-NIL." + :type 'boolean) + (defcustom brief-window-adjustment-timeout 30 "Brief window edge adjustment loop timeout. In the window edge adjustment loop, Brief waits only for arrow keys, Enter, @@ -2711,27 +2723,30 @@ from cursor." (interactive) (brief-adjust-window-edge-loop 'right)) +;;; Support functions for `brief-merge-neighbor-window' -;; Support functions for `brief-merge-neighbor-window' -;; In the following, each `w' represent a brief-window (a window or a group) -;; and `#' represents an integer as the id -;; for window: -;; w# = (x0 y0 x1 y1 &winf), edges of window # -;; group format: ((dir wg &winf) grouplist) -;; ((dir gx0 gy0 gx1 gy1 &winf) (w0x0 w0y0 w0x1 w0y1) (w1x0 w1y0 w1x1 w1y1) -;; ...) -;; where -;; dir: nil:vertical, non-nil:horizontal +;; +;; In the following, each `w' represent a Brief-window (a window or a group) +;; and `#' represents an integer as the id. +;; For Brief windows: +;; w# = (x0 y0 x1 y1 &winf), edges of window # +;; Brief group format: ((dir wg &winf) grouplist) +;; ex: ((dir gx0 gy0 gx1 gy1 &winf) (w0x0 w0y0 w0x1 w0y1) ; win0 edges +;; (w1x0 w1y0 w1x1 w1y1) ; win1 edges +;; ...) +;; where +;; dir: group direction, nil:vertical, non-nil:horizontal ;; wg: (x0 y0 x1 y1): window edges of this group -;; &winf: optional Emacs window information: -;; (buf start point dedicated wid) -;; grouplist: (w0 w1...) -;; w#: (x0 y0 x1 y1 &winf): edges of window # +;; &winf: optional Emacs window information (see also `brief-window-bufinfo'): +;; (start point hscroll state overlay atomicity wid) +;; grouplist: w0 w1 ..., the brief window list of this group +;; . w#: (x0 y0 x1 y1 &winf): edges of window # ;; . Each group are of the same width or height in `dir'ection ;; . Group can be multi-level, meaning it can contain sub-groups and windows ;; . A single window must not be formed as a group, hence group should contain ;; at least two sub-groups or windows and its length is thus always >= 3 of ;; the above format (gi w0 w1 ...) where gi=(dir wg &winf) +;; (defconst brief-merge-window-tolerance 1 "Delete and merge neighboring windows without exact matching sides. @@ -2749,12 +2764,10 @@ rate. Notice that this value must not be negative.") ;; equivalent to `brief>=' and vice versa; so does `not brief>' ;; is not equivalent to `brief<=' and vice versa. -(defmacro brief-tolerant (v1 v2) +(defmacro brief= (v1 v2) ;; v1 and v2 and are within the tolerance in difference `(<= (abs (- ,v1 ,v2)) brief-merge-window-tolerance)) -(defalias 'brief= #'brief-tolerant) - (defmacro brief< (l r) ;; tolerance considered `(> (- ,r ,l) brief-merge-window-tolerance)) @@ -2794,10 +2807,10 @@ rate. Notice that this value must not be negative.") w) 0 4)) -(defmacro brief-win-left (w) `(first (brief-win-edges ,w))) -(defmacro brief-win-right (w) `(third (brief-win-edges ,w))) -(defmacro brief-win-top (w) `(second (brief-win-edges ,w))) -(defmacro brief-win-bottom (w) `(fourth (brief-win-edges ,w))) +(defmacro brief-win-left (w) `(cl-first (brief-win-edges ,w))) +(defmacro brief-win-right (w) `(cl-third (brief-win-edges ,w))) +(defmacro brief-win-top (w) `(cl-second (brief-win-edges ,w))) +(defmacro brief-win-bottom (w) `(cl-fourth (brief-win-edges ,w))) (defun brief-merge-edges (w1 w2) ;; w1 and w2 must be either vertically or horizontally aligned @@ -2821,20 +2834,20 @@ Matching is determined by a tolerance value to make window merging easier for the users." (if horizontal ;; y coordinate match - (and (brief-tolerant (brief-win-top w1) (brief-win-top w2)) - (brief-tolerant (brief-win-bottom w1) (brief-win-bottom w2))) + (and (brief= (brief-win-top w1) (brief-win-top w2)) + (brief= (brief-win-bottom w1) (brief-win-bottom w2))) ;; x coordinate match - (and (brief-tolerant (brief-win-left w1) (brief-win-left w2)) - (brief-tolerant (brief-win-right w1) (brief-win-right w2))))) + (and (brief= (brief-win-left w1) (brief-win-left w2)) + (brief= (brief-win-right w1) (brief-win-right w2))))) (defun brief-win-followed (w1 w2 &optional horiz) ;; Note that W1 W2 must be aligned in the direction first, ;; check if W1 is followed by W2 (if horiz ;; horizontally mergeable, w2 follows w1 - (brief-tolerant (brief-win-left w2) (brief-win-right w1)) + (brief= (brief-win-left w2) (brief-win-right w1)) ;; vertical mergeable - (brief-tolerant (brief-win-top w2) (brief-win-bottom w1)))) + (brief= (brief-win-top w2) (brief-win-bottom w1)))) (defun brief-win-mergeable (w1 w2 &optional horiz) "Check if two Brief windows W1 and W2 can be merged in direction HORIZ. @@ -2865,15 +2878,111 @@ both aligned and neighboring." (sort wlist (lambda (a b) (> (brief-win-top a) (brief-win-top b)))) (sort wlist (lambda (a b) (< (brief-win-top a) (brief-win-top b))))))) +(defun brief-window-atomic-p (wid) + ;; Check if Emacs window WID is atomic + (cdr (assoc 'window-atom (window-parameters wid)))) + +(defun brief-force-delete-window (wid) + ;; For atomic windows, clear its window-atom property and delete it + (if (brief-window-atomic-p wid) + (set-window-parameter wid 'window-atom nil)) + (delete-window wid)) + (defun brief-window-bufinfo (wid) "Window associated buffer information to be restored upon reconstruction." ;; WID is Emacs window object - (list (window-buffer wid) - (window-start wid) - (window-point wid) - (window-dedicated-p wid) - (window-hscroll wid) - wid)) + (if (window-live-p wid) + ;; Restoring the window state alone does not guarantee restore the window + ;; start/point and hscroll even if window states do contain these values + (list (window-start wid) + (window-point wid) + (window-hscroll wid) + ;; Whole window state + (window-state-get wid) + ;; Window only overlays + (let (ov) + ;; Atomicity: need search the whole buffer even it's huge + (with-current-buffer (window-buffer wid) + (dolist (o (overlays-in (point-min) (point-max)) ov) + (when (eq (overlay-get o 'window) wid) + (overlay-put o 'window nil) + (push o ov)))) + (nreverse ov)) + ;; Atomicity + (brief-window-atomic-p wid) + ;; Emacs window ID, mainly for debug + wid) + ;; internal window + (list nil nil nil nil nil (brief-window-atomic-p wid) wid))) + +(defun brief-win-add-winfo (bw ew) + ;; Extract &winf from Emacs window EW into Brief window BW + (append bw (list (brief-window-bufinfo ew)))) + +(defun brief-atomic-window-group (ew) + ;; EW is an Atomic Emacs window + ;; return (brief-group ewlist) where ewlist is the list of live windows + (cl-assert (window-valid-p ew) t "not a valid Emacs window") + (if (window-live-p ew) + ;; Leaf + (list (brief-win-add-winfo (window-edges ew) ew) ew) + ;; Internal windows + (let (bg res ech dir ewlist) + (if (setq ech (window-left-child ew)) + ;; horizontal combination + (setq dir t) + ;; vertical combination + (setq ech (window-top-child ew) + dir nil)) + (setq ewlist (brief-atomic-window-group ech) + bg (car ewlist) + ewlist (cdr ewlist)) + (while (setq ech (window-next-sibling ech)) + (setq res (brief-atomic-window-group ech)) + (setq bg (brief-merge-win bg (car res) dir) + ewlist (append ewlist (cdr res)))) + (cl-assert (brief-group-p bg) t "bg must be a Brief group") + (cl-assert (brief-win-match (brief-win-edges bg) (window-edges ew)) + t "bg does not match ew edges") + (cons (cons (brief-win-add-winfo (car bg) ew) + (cdr bg)) + ewlist)))) + +(defun brief-window-list (ewinlis) + ;; Convert Emacs window list to Brief window list, + ;; and a list of Brief atomic window groups + (let (w bwinlis bawinlis ; bawinlis: brief atomic tree list + eawinlis ar) ; Emacs atomic tree list, atomic root + (while ewinlis + (setq w (car ewinlis) + ewinlis (cdr ewinlis)) + + (if (setq ar (brief-window-atomic-p w)) ; also clear AR to NIL + (if (setq ar (window-atom-root w)) + (if (member ar eawinlis) + (setq ar nil) ; already processed + (setq eawinlis (cons ar eawinlis) + bawinlis (cons (brief-win-add-winfo (window-edges ar) ar) + bawinlis))))) + + (if (and ar brief-merge-window-respect-atomicity) + ;; Construct the atomic window tree and remove them from EWINLIS. + (let (bg ewlist) + (setq ewlist (brief-atomic-window-group ar) + bg (car ewlist) + ewlist (cdr ewlist)) + ;; Remove all windows in ewlist from ewinlis + (dolist (aw ewlist) + (unless (eq aw w) + ;; If the following assertion is violated, the construction of the + ;; input list EWINLIS is problematic. + (cl-assert (member aw ewinlis) t + "internal error: AW must be contained in EWINLIS") ; DBG + (setq ewinlis (delete aw ewinlis)))) + (setq bwinlis (cons bg bwinlis))) + (setq bwinlis + (cons (brief-win-add-winfo (window-edges w) w) bwinlis)))) + (list bwinlis bawinlis))) (defun brief-ancestor-p (anc win) (while (and win @@ -2890,15 +2999,21 @@ both aligned and neighboring." (catch 'found (while p (if (member p a1) - (throw 'found p) + ;; For atomic windows, if `brief-merge-window-respect-atomicity' + ;; is non-NIL, choose the `window-atom-root' if common ancestor + ;; found is the child of the `window-atom-root'. + (if (and brief-merge-window-respect-atomicity + (brief-window-atomic-p p)) + (throw 'found (window-atom-root p)) + (throw 'found p)) (setq p (window-parent p)))) nil))) (defun brief-win-match (w1 w2) - (and (brief-tolerant (brief-win-left w1) (brief-win-left w2)) - (brief-tolerant (brief-win-right w1) (brief-win-right w2)) - (brief-tolerant (brief-win-top w1) (brief-win-top w2)) - (brief-tolerant (brief-win-bottom w1) (brief-win-bottom w2)))) + (and (brief= (brief-win-left w1) (brief-win-left w2)) + (brief= (brief-win-right w1) (brief-win-right w2)) + (brief= (brief-win-top w1) (brief-win-top w2)) + (brief= (brief-win-bottom w1) (brief-win-bottom w2)))) (defun brief-win-exact-match (w1 w2) (and (= (brief-win-left w1) (brief-win-left w2)) @@ -2960,55 +3075,100 @@ both aligned and neighboring." (winf (car (nthcdr 4 node)))) (when winf ;; Refers to `brief-window-bufinfo' - (let* ((buf (first winf)) - (start (second winf)) - (point (third winf)) - (dedic (fourth winf)) - (hscr (fifth winf)) + (let* ((start (cl-first winf)) + (point (cl-second winf)) + (hscr (cl-third winf)) + (state (cl-fourth winf)) + (ovr (cl-fifth winf)) (we (window-edges)) delta) (unless (brief-win-exact-match we node) ;; Resize window to adjust sides (if (/= 0 (setq we (window-edges) delta (- (brief-win-width node) - (- (third we) (first we))))) + (- (cl-third we) (cl-first we))))) (ignore-errors (window-resize w delta t t))) (if (/= 0 (setq we (window-edges) delta (- (brief-win-height node) - (- (fourth we) (second we))))) + (- (cl-fourth we) (cl-second we))))) (ignore-errors (window-resize w delta nil t)))) - (set-window-buffer-start-and-point w buf start point) - (set-window-dedicated-p w dedic) + ;; Restore all window states + (window-state-put state w) + ;; Restore window specific overlays + (dolist (o ovr) + (overlay-put o 'window w)) + (set-window-buffer-start-and-point w (window-buffer w) + start point) (set-window-hscroll w hscr)))))) -(defun brief-window-layout (root from &optional reconstruct) +(defun brief-win-search (aw node) + ;; Search Brief window AW within NODE + (if (brief-group-p node) + ;; Brief window group + (catch 'found + (dolist (bw (cdr node)) ; children + (if (setq node (brief-win-search aw bw)) + (throw 'found node)))) + ;; Brief window + (when (brief-win-match aw node) + node))) + +(defun brief-window-layout (root from &optional reconstruct bawinlis) "Resplit windows of current frame and re-split according to ROOT" ;; ROOT is of brief window format (window or group) ;; Successfully merged into an unique ROOT, start re-spliting windows - (let ((efrom (window-edges from)) - (fded (window-dedicated-p from)) - ;; [2024-03-13 Wed] Setting `split-window-keep-point' nil - ;; should enhance the performance of re-displaying. This is - ;; okay as in the end we'll always restore window point. - (split-window-keep-point nil) - ;; prevent special settings get into the way - (ignore-window-parameters t) - ;; prevent customized settings - (window-combination-limit 'window-size)) + (let* ((efrom (window-edges from)) + (fded (window-dedicated-p from)) + ;; [2024-03-13 Wed] Setting `split-window-keep-point' nil + ;; should enhance the performance of re-displaying. This is + ;; okay as in the end we'll always restore window point. + (split-window-keep-point nil) + ;; prevent special settings get into the way + (ignore-window-parameters t) + ;; prevent customized settings + (window-combination-limit 'window-size)) + + ;; Check if atomic window still valid within this layout + (if (and bawinlis brief-merge-window-respect-atomicity) + (dolist (aw bawinlis) + (if (not (brief-win-search aw root)) + ;; any possibility not able to restore atomicity, abort. + (user-error (format +"Merge aborted due to breaking atomic window at text coordinate (%d,%d)." + (brief-win-left aw) (brief-win-top aw)))))) + ;; Close all windows (if reconstruct (dolist (w reconstruct) - (if (not (eq w from)) (delete-window w))) + (if (not (eq w from)) (brief-force-delete-window w))) (walk-windows (lambda (w) - (if (not (eq w from)) (delete-window w))))) + (if (not (eq w from)) + (brief-force-delete-window w))))) ;; After closing windows, the sole window is FROM + ;; TODO: restore other window parameters. No need to restore the whole + ;; window state for FROM as it's the only one window not deleted. + (if (brief-window-atomic-p from) + (set-window-parameter from 'window-atom nil)) (set-window-dedicated-p from nil) + ;; Re-split windows (brief-traverse-layout root) + + ;; Restore window atomicity if feasible, according to BAWINLIS. + ;; Notice that this restoration may restore FROM's as well, because its + ;; sibling atomic window's parent should be saved in BAWINLIS already. + (dolist (aw bawinlis) + (catch 'atomic-window + (dolist (paw (brief-ancestors (window-at (brief-win-left aw) + (brief-win-top aw)))) + (if (brief-win-match aw (window-edges paw)) + ;; Atomicity intact, restore + (throw 'atomic-window (window-make-atom paw)))))) + ;; Jump back to the merged win where we FROM - (select-window (window-at ;; center of FROM + (select-window (window-at ; center of FROM (+ (brief-win-left efrom) (brief/2 (brief-win-width efrom))) (+ (brief-win-top efrom) @@ -3047,7 +3207,7 @@ both aligned and neighboring." ;; Both are groups, check their directions (setq dsrc (brief-group-dir src) dtgt (brief-group-dir target)) - (if (xor dsrc dtgt) + (if (not (eq dsrc dtgt)) ;; Both groups are of different direction (if (eq dsrc dir) ;; Same direction as src, merge target into src @@ -3115,7 +3275,7 @@ both aligned and neighboring." ;; the input windows; return nil if fail. ;; This function take a shortcut, on traversing all possible ;; solutions it will throw the first solution found and cut the rest - ;; search tree just like the cut `!' operator in Prologue language. + ;; search tree just like the cut `!' operator in the Prolog language. ;; Note: This operation is non-destructive to BWINLIST @@ -3250,9 +3410,9 @@ both aligned and neighboring." center))))))))) (dolist (splitinfo splitpoints) - (setq pcurr (first splitinfo) - pprev (second splitinfo) - horiz (third splitinfo)) + (setq pcurr (cl-first splitinfo) + pprev (cl-second splitinfo) + horiz (cl-third splitinfo)) (cl-assert (equal (cdr pprev) pcurr) t "Node arrangement of the list cutting point is not as expected") ; DBG ;; Cut SLIST0 into two groups @@ -3294,18 +3454,28 @@ which is by default enabled. Also check its documentation string for more detail and restriction on emulating this Brief editor behavior on Emacs." (when (and to (not (eq (selected-window) to))) + + ;; Check if `to' is a atomic window, if yes, when + ;; `brief-merge-window-respect-atomicity' is non-NIL we indicate + ;; the user that merging failed due to atomicity. + (if (and brief-merge-window-respect-atomicity + (brief-window-atomic-p to)) + (user-error + "Cannot merge atomic windows; prefix with C-u to delete them.")) + (let* ((from (selected-window)) (pfrom (window-parent from)) (pto (window-parent to)) (wfrom (window-edges from)) (wto (window-edges to)) + ;; Dynamically bind system variable (window-combination-limit 'window-size)) ;; Check if edges match (if (brief-aligned-p wfrom wto horiz) (if (eq pfrom pto) ;; Same parent, merge two windows directly ;; Edges match and same parent, saving all edges of siblings - (let (siblis ;; siblings list, associated with size + (let (siblis ; siblings list, associated with size (sib (window-child (window-parent)))) ;; Record all edges except TO (while sib @@ -3320,7 +3490,21 @@ Emacs." siblis))) (setq sib (window-next-sibling sib))) ;; Delete TO - (delete-window to) + (if (brief-window-atomic-p to) + ;; Only if `brief-merge-window-respect-atomicity' + ;; is NIL we are able to reach here, thus that setting + ;; choose merging outweighs atomicity here. + (let ((patom (window-atom-root to))) + ;; Window deletion under the same parent, allow merge + (set-window-parameter to 'window-atom nil) + ;; This deletion will destroy the atomicity of the + ;; whole atomic tree, re-enable it after merge + (delete-window to) + ;; Restore atomicity if there are still atomic windows + (if (window-valid-p patom) + (window-make-atom patom))) + ;; Not atomic, just delete it + (delete-window to)) ;; Sort windows according to X/Y ordering (setq siblis (brief-sort-winpos siblis horiz)) ;; Restore all sibling windows in sorted X/Y ordering @@ -3328,7 +3512,7 @@ Emacs." ;; by latter ones. (dolist (sib siblis) ;; x0 y0 x1 y1 w - (let* ((win (fifth sib)) + (let* ((win (cl-fifth sib)) (delta (if horiz (- (- (brief-win-right sib) (brief-win-left sib)) @@ -3343,7 +3527,7 @@ Emacs." ;; Different parents, combining windows belongs to ;; different parents and then reconstruct windows - ;; accordingly. w/ bottom-up re-construction + ;; accordingly. (let* ((cmnanc (brief-first-common-ancestor from to)) (range (window-edges cmnanc)) ;; Emacs window list to be constructed @@ -3352,21 +3536,19 @@ Emacs." (target (append (brief-merge-edges wfrom wto) (list (brief-window-bufinfo from)))) ;; Brief window list to be constructed - (bwinlis (list target))) + bwinlis bawinlis) ;; Build brief window list BWINLIS from Emacs window list EWINLIS - (dolist (w ewinlis) - (if (not (or (eq w from) (eq w to))) - (setq bwinlis - (cons (append (window-edges w) - (list (brief-window-bufinfo w))) - bwinlis)))) + (setq bawinlis (brief-window-list + (remove from (remove to ewinlis))) + bwinlis (cons target (car bawinlis)) + bawinlis (cadr bawinlis)) ; atomic groups to be restored (setq target (catch 'unified (brief-split-merge bwinlis range))) (if target - (brief-window-layout target from ewinlis) + (brief-window-layout target from ewinlis bawinlis) (if brief-delete-window-on-merge-failure (delete-window to) (message @@ -3384,34 +3566,34 @@ customized variable `brief-merge-deleted-window'. When prefixed, execute the other operation specified by `brief-merge-deleted-window'. Before Brief v5.90 the only operation supported is deletion." (interactive) - (if (xor brief-merge-deleted-window - current-prefix-arg) - (brief-merge-neighbor-window (brief-upside-window)) - (delete-window (brief-upside-window)))) + (if (eq brief-merge-deleted-window + (not (null current-prefix-arg))) + (delete-window (brief-upside-window)) + (brief-merge-neighbor-window (brief-upside-window)))) (defun brief-delete-window-down () "Merge or delete the bottom side neighboring window." (interactive) - (if (xor brief-merge-deleted-window - current-prefix-arg) - (brief-merge-neighbor-window (brief-downside-window)) - (delete-window (brief-downside-window)))) + (if (eq brief-merge-deleted-window + (not (null current-prefix-arg))) + (delete-window (brief-downside-window)) + (brief-merge-neighbor-window (brief-downside-window)))) (defun brief-delete-window-left () "Merge or delete the left side neighboring window." (interactive) - (if (xor brief-merge-deleted-window - current-prefix-arg) - (brief-merge-neighbor-window (brief-leftside-window) 'horizontal) - (delete-window (brief-leftside-window)))) + (if (eq brief-merge-deleted-window + (not (null current-prefix-arg))) + (delete-window (brief-leftside-window)) + (brief-merge-neighbor-window (brief-leftside-window) 'horizontal))) (defun brief-delete-window-right () "Merge or delete the right side neighboring window." (interactive) - (if (xor brief-merge-deleted-window - current-prefix-arg) - (brief-merge-neighbor-window (brief-rightside-window) 'horizontal) - (delete-window (brief-rightside-window)))) + (if (eq brief-merge-deleted-window + (not (null current-prefix-arg))) + (delete-window (brief-rightside-window)) + (brief-merge-neighbor-window (brief-rightside-window) 'horizontal))) (defun brief-delete-current-window () (interactive) @@ -4706,10 +4888,8 @@ replaced by the marking texts; when operation canceled we will not be able to restore it back if we have no backups.") ;; The core modification that prevent Windows X server failure -;; due to too much flooding message as clipboard change -;; -;; `advice-add' defined -;; +;; due to too much flooding message as clipboard change. + ;; When external helper is enabled but neither 'xsel' nor 'xclip' is ;; installed, reenter will occur. (defvar brief-gui-get-selection-reentry nil