From 49c48af0ed75283d23dd91d516fdaa9fb8541563 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Tue, 1 Mar 2022 16:20:09 -0500 Subject: Add perspective buffer list merging. --- perspective.el | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/perspective.el b/perspective.el index d51f09c..281eaab 100644 --- a/perspective.el +++ b/perspective.el @@ -1675,11 +1675,13 @@ PERSP-SET-IDO-BUFFERS)." ;; :order [...] ;; } ;; ] +;; :merge-list ;; } (cl-defstruct persp--state-complete files - frames) + frames + merge-list) (cl-defstruct persp--state-frame persps @@ -1840,7 +1842,8 @@ visible in a perspective as windows, they will be saved as (persp-save) (let ((state-complete (make-persp--state-complete :files (persp--state-file-data) - :frames (persp--state-frame-data)))) + :frames (persp--state-frame-data) + :merge-list persp-merge-list))) ;; create or overwrite target-file: (with-temp-file target-file (prin1 state-complete (current-buffer)))) ;; after hook @@ -1897,6 +1900,8 @@ restored." (window-state-put (persp--state-single-windows state-single) (frame-root-window (selected-frame)) 'safe))))) + ;; restore merge-list + (setq persp-merge-list (persp--state-complete-merge-list state-complete)) ;; cleanup (persp-kill tmp-persp-name)) ;; after hook @@ -1905,6 +1910,105 @@ restored." (defalias 'persp-state-restore 'persp-state-load) + ;;; --- perspective merging + +(defvar persp-merge-list nil + "A list of the all the current perspective merges.") + +(defun persp-get-merge (base-name merged-name) + "Return a merge with :base-perspective BASE-NAME and :merged-perspective +MERGED-NAME." + (cl-find-if + (lambda (m) + (and (string= base-name (plist-get m :base-perspective)) + (string= merged-name (plist-get m :merged-perspective)))) + persp-merge-list)) + +(defun persp-merges-with-base (&optional name) + "Return a list of all merges in `persp-merge-list' with base perspective +NAME." + (if (null name) (setq name (persp-current-name))) + (cl-remove-if-not + (lambda (m) + (string= name (plist-get m :base-perspective))) + persp-merge-list)) + +(defun persp-perspectives-merged-with-base (&optional name) + "Return a list of all perspectives that are merged to NAME." + (if (null base-persp-name) (setq name (persp-current-name))) + (mapcar (lambda (m) (plist-get m :merged-perspective)) + (persp-merges-with-base name))) + +(defun persp-merge (base-persp-name to-merge-persp-name) + "Merge the buffer list of TO-MERGE-PERSP-NAME into the buffer list for +BASE-PERSP-NAME." + (interactive + (list (persp-current-name) + (funcall persp-interactive-completion-function + "Perspective name: " + (remove (persp-current-name) (persp-all-names)) + nil + t))) + (cl-assert (member base-persp-name (persp-all-names))) + (cl-assert (member to-merge-persp-name (persp-all-names))) + (let* ((merge (persp-get-merge base-persp-name to-merge-persp-name)) + (all-to-merge-persp-buffers (persp-get-buffer-names to-merge-persp-name)) + (merged-into-to-merge-persp-buffers (cl-loop for m in (persp-merges-with-base to-merge-persp-name) + append (plist-get m :merged-buffers))) + (buffers-to-merge (delete-dups + (cl-remove-if + (lambda (buf) + (or (member buf merged-into-to-merge-persp-buffers) + (string= buf (persp-scratch-buffer to-merge-persp-name)))) + all-to-merge-persp-buffers)))) + (with-perspective base-persp-name + (if merge + ;; update an existing merge + (let ((merged-buffers (plist-get merge :merged-buffers))) + (dolist (buf buffers-to-merge) + (unless (persp-is-current-buffer (get-buffer buf)) + (persp-add-buffer buf) + (push buf merged-buffers))) + (cl-nsubstitute-if (list :base-perspective base-persp-name + :merged-perspective to-merge-persp-name + :merged-buffers merged-buffers) + (lambda (m) (equal merge m)) + persp-merge-list)) + ;; create a new merge + (let ((merged-buffers)) + (dolist (buf buffers-to-merge) + (unless (persp-is-current-buffer (get-buffer buf)) + (persp-add-buffer buf) + (push buf merged-buffers))) + (add-to-list 'persp-merge-list (list :base-perspective base-persp-name + :merged-perspective to-merge-persp-name + :merged-buffers merged-buffers))))))) + +(defun persp-unmerge (base-persp-name to-unmerge-persp-name) + "Unmerge the buffers from TO-UNMERGE-PERSP-NAME from BASE-PERSP-NAME that were +were merged in from a previous call to `persp-merge'." + (interactive + (let* ((base-persp-name (persp-current-name)) + (persps-merged-with-base (persp-perspectives-merged-with-base base-persp-name)) + (to-unmerge-persp-name + (when persps-merged-with-base + (funcall persp-interactive-completion-function + "Perspective name: " + persps-merged-with-base + nil + t)))) + (list base-persp-name to-unmerge-persp-name))) + (let ((merge (persp-get-merge base-persp-name to-unmerge-persp-name))) + (cond ((null to-unmerge-persp-name) + (message "No perspectives merged to \"s\"" base-persp-name)) + ((null merge) + (message "\"%s\" is not merged to \"%s\"" to-unmerge-persp-name base-persp-name)) + (t (with-perspective base-persp-name + (dolist (buf (plist-get merge :merged-buffers)) + (persp-remove-buffer buf)) + (setq persp-merge-list (remove merge persp-merge-list))))))) + + ;;; --- ibuffer filter group code (with-eval-after-load 'ibuffer -- cgit v1.0 From bfc1ae1db3f5d9752e00fb55ce21b4e0d16e5af8 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Tue, 1 Mar 2022 16:20:40 -0500 Subject: Test persp-merge and persp-unmerge. --- test/test-perspective.el | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/test-perspective.el b/test/test-perspective.el index d5a7005..f12caa3 100644 --- a/test/test-perspective.el +++ b/test/test-perspective.el @@ -2260,4 +2260,39 @@ persp-test-make-sample-environment." (persp-test-check-sample-environment)) (persp-test-clean-files "A1" "A2" "A3" "B1" "B2" "B3" "B4" "state-1.el"))) +(ert-deftest merge-and-unmerge () + (let ((persp-merge-list nil)) + (unwind-protect + (persp-test-with-persp + (persp-test-with-files nil (A1 A2 B1 B2 C1 C2) + (with-named-persp "A" + (persp-add-buffer "A1") + (persp-add-buffer "A2") + (with-named-persp "B" + (persp-add-buffer "B1") + (persp-add-buffer "B2") + (with-named-persp "C" + (persp-add-buffer "C1") + (persp-add-buffer "C2") + ;; basic merging + (persp-merge "B" "A") + (should (equal (list (persp-scratch-buffer "B") "A1" "A2" "B1" "B2") + (sort (persp-get-buffer-names "B") #'string-lessp))) + ;; merging are not transitive + (persp-merge "C" "B") + (should (equal (list (persp-scratch-buffer "C") "B1" "B2" "C1" "C2") + (sort (persp-get-buffer-names "C") #'string-lessp))) + ;; basic unmerging + (persp-unmerge "C" "B") + (should (equal (list (persp-scratch-buffer "C") "C1" "C2") + (sort (persp-get-buffer-names "C") #'string-lessp))) + ;; don't unmerge buffers that were in base before the merge + (with-perspective "C" + (persp-add-buffer "A1")) + (persp-merge "C" "A") + (persp-unmerge "C" "A") + (should (equal (list (persp-scratch-buffer "C") "A1" "C1" "C2") + (sort (persp-get-buffer-names "C") #'string-lessp)))))))) + (persp-test-clean-files "A1" "A2" "B1" "B2" "C1" "C2")))) + ;;; test-perspective.el ends here -- cgit v1.0 From e98eb8fa4a6152cf0a40e1e6f2d4c1f203fbd709 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Tue, 1 Mar 2022 23:13:25 -0500 Subject: Fixed variable name typo. --- perspective.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/perspective.el b/perspective.el index 281eaab..4e03c60 100644 --- a/perspective.el +++ b/perspective.el @@ -1935,7 +1935,7 @@ NAME." (defun persp-perspectives-merged-with-base (&optional name) "Return a list of all perspectives that are merged to NAME." - (if (null base-persp-name) (setq name (persp-current-name))) + (if (null name) (setq name (persp-current-name))) (mapcar (lambda (m) (plist-get m :merged-perspective)) (persp-merges-with-base name))) -- cgit v1.0 From c04bdcc6b3305e37dffbb46c0318c2d55f804d01 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Wed, 2 Mar 2022 08:41:12 -0500 Subject: Fixed typo in error message. --- perspective.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/perspective.el b/perspective.el index 4e03c60..886e709 100644 --- a/perspective.el +++ b/perspective.el @@ -2000,7 +2000,7 @@ were merged in from a previous call to `persp-merge'." (list base-persp-name to-unmerge-persp-name))) (let ((merge (persp-get-merge base-persp-name to-unmerge-persp-name))) (cond ((null to-unmerge-persp-name) - (message "No perspectives merged to \"s\"" base-persp-name)) + (message "No perspectives merged to \"%s\"" base-persp-name)) ((null merge) (message "\"%s\" is not merged to \"%s\"" to-unmerge-persp-name base-persp-name)) (t (with-perspective base-persp-name -- cgit v1.0 From 36fcf71d18990f31340b6ce8d8bbe50745aac786 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Thu, 3 Mar 2022 13:56:18 -0500 Subject: Document perspective merging. --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index 2c9eeee..0150273 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ please refer to it for release notes. - [Sample Use Cases](#sample-use-cases) - [Multiple Projects](#multiple-projects) - [Yak Shaving](#yak-shaving) + - [Perspective Merging](#perspective-merging) - [Similar Packages](#similar-packages) - [Compatibility](#compatibility) - [Installation](#installation) @@ -83,6 +84,35 @@ Y, you close perspective `bugfix-Y` and return to `feature-X`. — see documentation below.) +### [Perspective Merging] + +Yak shaving is useful for working on projects that are largely unrelated but +sometimes you are working on multiple projects that are very much related, to +the point that you want to view files from both projects at the same time. This +is where perspective merging comes in. + +Suppose you are working on a project that requires developing multiple auxiliary +libraries. It may get messy to develop both the main project and all the +libraries from the same perspective so instead you put each library in its own +perspective so you can work on them in isolation. All of a sudden though you +wish to see library code from the main projects perspective. Instead of +switching back and forth between the library and main projects perspectives you +can run `M-x persp-merge` and import the buffers from the libraries perspective. +When you are done you can run remove the imported buffers with +`M-x persp-unmerge`. + +The purpose of perspective merging is to combine the buffer lists of different +perspectives while keeping a clear distinction of which buffers belong to which +perspective. + +- You can merge together as many perspectives as you want. +- Merging is one directional so if you merge A into B, B's buffers will not be +available in A. +- Merging is not transitive so if you merge A into B, then B into C, the buffers +in A will not be available in C. +- The merge state is saved across sessions when using [persp-state-{save,load}](#saving-sessions-to-disk). + + ## Similar Packages The following Emacs packages implement comparable functionality: -- cgit v1.0 From 183e3a3b5bae9f6be4892ad5c3e9452b7139cdf6 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Fri, 4 Mar 2022 23:04:42 -0500 Subject: Remove brackets around perspective-merging header. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0150273..3e30284 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Y, you close perspective `bugfix-Y` and return to `feature-X`. — see documentation below.) -### [Perspective Merging] +### Perspective Merging Yak shaving is useful for working on projects that are largely unrelated but sometimes you are working on multiple projects that are very much related, to -- cgit v1.0 From eef4b6dbaeba5d9c78344370c1211ae99bd2a5b5 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Tue, 8 Mar 2022 10:41:04 -0500 Subject: Implement persp-merge-list as a frame parameter. --- perspective.el | 64 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/perspective.el b/perspective.el index 886e709..db72b62 100644 --- a/perspective.el +++ b/perspective.el @@ -1912,32 +1912,28 @@ restored." ;;; --- perspective merging -(defvar persp-merge-list nil - "A list of the all the current perspective merges.") - -(defun persp-get-merge (base-name merged-name) - "Return a merge with :base-perspective BASE-NAME and :merged-perspective -MERGED-NAME." +(defun persp-get-merge (base-name merged-name &optional frame) + "Return a merge in FRAME with :base-perspective BASE-NAME and +:merged-perspective MERGED-NAME." (cl-find-if (lambda (m) (and (string= base-name (plist-get m :base-perspective)) (string= merged-name (plist-get m :merged-perspective)))) - persp-merge-list)) + (frame-parameter frame 'persp-merge-list))) -(defun persp-merges-with-base (&optional name) - "Return a list of all merges in `persp-merge-list' with base perspective -NAME." +(defun persp-merges-with-base (&optional name frame) + "Return a list of all merges in FRAME with base perspective NAME." (if (null name) (setq name (persp-current-name))) (cl-remove-if-not (lambda (m) (string= name (plist-get m :base-perspective))) - persp-merge-list)) + (frame-parameter frame 'persp-merge-list))) -(defun persp-perspectives-merged-with-base (&optional name) - "Return a list of all perspectives that are merged to NAME." +(defun persp-perspectives-merged-with-base (&optional name frame) + "Return a list of all perspectives in FRAME that are merged to NAME." (if (null name) (setq name (persp-current-name))) (mapcar (lambda (m) (plist-get m :merged-perspective)) - (persp-merges-with-base name))) + (persp-merges-with-base name frame))) (defun persp-merge (base-persp-name to-merge-persp-name) "Merge the buffer list of TO-MERGE-PERSP-NAME into the buffer list for @@ -1946,11 +1942,9 @@ BASE-PERSP-NAME." (list (persp-current-name) (funcall persp-interactive-completion-function "Perspective name: " - (remove (persp-current-name) (persp-all-names)) - nil - t))) - (cl-assert (member base-persp-name (persp-all-names))) - (cl-assert (member to-merge-persp-name (persp-all-names))) + (remove (persp-current-name) (persp-names)) nil t))) + (cl-assert (member base-persp-name (persp-names))) + (cl-assert (member to-merge-persp-name (persp-names))) (let* ((merge (persp-get-merge base-persp-name to-merge-persp-name)) (all-to-merge-persp-buffers (persp-get-buffer-names to-merge-persp-name)) (merged-into-to-merge-persp-buffers (cl-loop for m in (persp-merges-with-base to-merge-persp-name) @@ -1969,20 +1963,27 @@ BASE-PERSP-NAME." (unless (persp-is-current-buffer (get-buffer buf)) (persp-add-buffer buf) (push buf merged-buffers))) - (cl-nsubstitute-if (list :base-perspective base-persp-name - :merged-perspective to-merge-persp-name - :merged-buffers merged-buffers) - (lambda (m) (equal merge m)) - persp-merge-list)) + (set-frame-parameter + nil + 'persp-merge-list + (cl-nsubstitute-if (list :base-perspective base-persp-name + :merged-perspective to-merge-persp-name + :merged-buffers merged-buffers) + (lambda (m) (equal merge m)) + (frame-parameter nil 'persp-merge-list)))) ;; create a new merge (let ((merged-buffers)) (dolist (buf buffers-to-merge) (unless (persp-is-current-buffer (get-buffer buf)) (persp-add-buffer buf) (push buf merged-buffers))) - (add-to-list 'persp-merge-list (list :base-perspective base-persp-name - :merged-perspective to-merge-persp-name - :merged-buffers merged-buffers))))))) + (set-frame-parameter + nil + 'persp-merge-list + (push (list :base-perspective base-persp-name + :merged-perspective to-merge-persp-name + :merged-buffers merged-buffers) + (frame-parameter nil 'persp-merge-list)))))))) (defun persp-unmerge (base-persp-name to-unmerge-persp-name) "Unmerge the buffers from TO-UNMERGE-PERSP-NAME from BASE-PERSP-NAME that were @@ -1994,9 +1995,7 @@ were merged in from a previous call to `persp-merge'." (when persps-merged-with-base (funcall persp-interactive-completion-function "Perspective name: " - persps-merged-with-base - nil - t)))) + persps-merged-with-base nil t)))) (list base-persp-name to-unmerge-persp-name))) (let ((merge (persp-get-merge base-persp-name to-unmerge-persp-name))) (cond ((null to-unmerge-persp-name) @@ -2006,7 +2005,10 @@ were merged in from a previous call to `persp-merge'." (t (with-perspective base-persp-name (dolist (buf (plist-get merge :merged-buffers)) (persp-remove-buffer buf)) - (setq persp-merge-list (remove merge persp-merge-list))))))) + (set-frame-parameter + nil + 'persp-merge-list + (remove merge (frame-parameter nil 'persp-merge-list)))))))) ;;; --- ibuffer filter group code -- cgit v1.0 From 81dfd5fd7b9c7d55ab9f69bc11cf9d3a9137ef18 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Tue, 8 Mar 2022 10:52:25 -0500 Subject: Undo changes to state saving. --- perspective.el | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/perspective.el b/perspective.el index db72b62..ae65db4 100644 --- a/perspective.el +++ b/perspective.el @@ -1675,13 +1675,11 @@ PERSP-SET-IDO-BUFFERS)." ;; :order [...] ;; } ;; ] -;; :merge-list ;; } (cl-defstruct persp--state-complete files - frames - merge-list) + frames) (cl-defstruct persp--state-frame persps @@ -1842,8 +1840,7 @@ visible in a perspective as windows, they will be saved as (persp-save) (let ((state-complete (make-persp--state-complete :files (persp--state-file-data) - :frames (persp--state-frame-data) - :merge-list persp-merge-list))) + :frames (persp--state-frame-data)))) ;; create or overwrite target-file: (with-temp-file target-file (prin1 state-complete (current-buffer)))) ;; after hook @@ -1900,8 +1897,6 @@ restored." (window-state-put (persp--state-single-windows state-single) (frame-root-window (selected-frame)) 'safe))))) - ;; restore merge-list - (setq persp-merge-list (persp--state-complete-merge-list state-complete)) ;; cleanup (persp-kill tmp-persp-name)) ;; after hook -- cgit v1.0 From 74d2d341d3b7484cede5bb8ad1f14fb3629e2df1 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Thu, 10 Mar 2022 13:19:03 -0500 Subject: Documentation grammar. --- test/test-perspective.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test-perspective.el b/test/test-perspective.el index f12caa3..5f521de 100644 --- a/test/test-perspective.el +++ b/test/test-perspective.el @@ -2278,7 +2278,7 @@ persp-test-make-sample-environment." (persp-merge "B" "A") (should (equal (list (persp-scratch-buffer "B") "A1" "A2" "B1" "B2") (sort (persp-get-buffer-names "B") #'string-lessp))) - ;; merging are not transitive + ;; merges are not transitive (persp-merge "C" "B") (should (equal (list (persp-scratch-buffer "C") "B1" "B2" "C1" "C2") (sort (persp-get-buffer-names "C") #'string-lessp))) -- cgit v1.0 From 085c268c7c01df9e92f13dafc0ec8efc535ee2e5 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Thu, 10 Mar 2022 14:13:21 -0500 Subject: Add support to persp-state-{save,load} to saving merge lists. --- perspective.el | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/perspective.el b/perspective.el index ae65db4..59fbd2e 100644 --- a/perspective.el +++ b/perspective.el @@ -1673,6 +1673,7 @@ PERSP-SET-IDO-BUFFERS)." ;; } ;; } ;; :order [...] +;; :merge-list ;; } ;; ] ;; } @@ -1685,10 +1686,32 @@ PERSP-SET-IDO-BUFFERS)." persps order) +(cl-defstruct persp--state-frame-v2 + persps + order + merge-list) + (cl-defstruct persp--state-single buffers windows) +(defun persp--state-complete-v2 (state-complete) + "If STATE-COMPLETE has a frames version 1 field then coerce its frames into +persp--state-frame-v2 struct." + (let* ((state-frames (persp--state-complete-frames state-complete)) + (state-frames-v2 + (mapcar (lambda (state-frame) + (if (persp--state-frame-v2-p state-frame) + state-frame + (make-persp--state-frame-v2 + :persps (persp--state-frame-persps state-frame) + :order (persp--state-frame-order state-frame) + :merge-list nil))) + state-frames))) + (make-persp--state-complete + :files (persp--state-complete-files state-complete) + :frames state-frames-v2))) + (defun persp--state-interesting-buffer-p (buffer) (and (buffer-name buffer) (not (string-match "^[[:space:]]*\\*" (buffer-name buffer))) @@ -1776,9 +1799,10 @@ to the perspective's *scratch* buffer." :buffers buffers :windows windows) persps-in-frame))))) - (make-persp--state-frame + (make-persp--state-frame-v2 :persps persps-in-frame - :order persp-names-in-order))))) + :order persp-names-in-order + :merge-list (frame-parameter nil 'persp-merge-list)))))) ;;;###autoload (cl-defun persp-state-save (&optional file interactive?) @@ -1868,10 +1892,11 @@ restored." ;; actually load (let ((tmp-persp-name (format "%04x%04x" (random (expt 16 4)) (random (expt 16 4)))) (frame-count 0) - (state-complete (read - (with-temp-buffer - (insert-file-contents file) - (buffer-string))))) + (state-complete (persp--state-complete-v2 + (read + (with-temp-buffer + (insert-file-contents file) + (buffer-string)))))) ;; open all files in a temporary perspective to avoid polluting "main" (persp-switch tmp-persp-name) (cl-loop for file in (persp--state-complete-files state-complete) do @@ -1882,8 +1907,11 @@ restored." (cl-incf frame-count) (when (> frame-count 1) (make-frame-command)) - (let* ((frame-persp-table (persp--state-frame-persps frame)) - (frame-persp-order (reverse (persp--state-frame-order frame)))) + (let* ((frame-persp-table (persp--state-frame-v2-persps frame)) + (frame-persp-order (reverse (persp--state-frame-v2-order frame))) + (frame-merge-list (persp--state-frame-v2-merge-list frame))) + ;; restore merge list + (set-frame-parameter nil 'persp-merge-list frame-merge-list) ;; iterate over the perspectives in the frame in the appropriate order (cl-loop for persp in frame-persp-order do (let ((state-single (gethash persp frame-persp-table))) -- cgit v1.0 From 3b6f0cd5efd255805a51b384a3483e34c04e9be9 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Thu, 10 Mar 2022 14:23:15 -0500 Subject: Document persp--state-complete-v2. --- perspective.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/perspective.el b/perspective.el index 59fbd2e..8619d42 100644 --- a/perspective.el +++ b/perspective.el @@ -1696,8 +1696,9 @@ PERSP-SET-IDO-BUFFERS)." windows) (defun persp--state-complete-v2 (state-complete) - "If STATE-COMPLETE has a frames version 1 field then coerce its frames into -persp--state-frame-v2 struct." + "Return a persp--state-complete struct based off of STATE-COMPLETE that is +guarenteed to be compatible with perspective state version 2 which supports +saving perspective merge lists." (let* ((state-frames (persp--state-complete-frames state-complete)) (state-frames-v2 (mapcar (lambda (state-frame) -- cgit v1.0 From 8820ca6f9ae65d6d652e5b10e1b5bbbb7329610f Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Thu, 10 Mar 2022 14:31:42 -0500 Subject: Document persp--state-complete-v2. --- perspective.el | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/perspective.el b/perspective.el index 8619d42..546689f 100644 --- a/perspective.el +++ b/perspective.el @@ -1682,6 +1682,7 @@ PERSP-SET-IDO-BUFFERS)." files frames) +;; Keep around old version to maintain backwards compatibility. (cl-defstruct persp--state-frame persps order) @@ -1696,9 +1697,9 @@ PERSP-SET-IDO-BUFFERS)." windows) (defun persp--state-complete-v2 (state-complete) - "Return a persp--state-complete struct based off of STATE-COMPLETE that is -guarenteed to be compatible with perspective state version 2 which supports -saving perspective merge lists." + "Apply this function to persp--state-complete structs to be guarenteed a +persp--state-complete that is compatible with merge-list saving. Useful for +maintaining backwards compatibility." (let* ((state-frames (persp--state-complete-frames state-complete)) (state-frames-v2 (mapcar (lambda (state-frame) -- cgit v1.0 From c08ee934116361ac026e209ac132028b9317ef12 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Thu, 10 Mar 2022 14:58:34 -0500 Subject: Fixed bug with merge-list frame parameter not being loaded. --- perspective.el | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/perspective.el b/perspective.el index 546689f..02a44f5 100644 --- a/perspective.el +++ b/perspective.el @@ -1908,12 +1908,11 @@ restored." (cl-loop for frame in (persp--state-complete-frames state-complete) do (cl-incf frame-count) (when (> frame-count 1) - (make-frame-command)) + (let ((new-frame (make-frame-command)) + (frame-merge-list (persp--state-frame-v2-merge-list frame))) + (set-frame-parameter new-frame 'merge-list frame-merge-list))) (let* ((frame-persp-table (persp--state-frame-v2-persps frame)) - (frame-persp-order (reverse (persp--state-frame-v2-order frame))) - (frame-merge-list (persp--state-frame-v2-merge-list frame))) - ;; restore merge list - (set-frame-parameter nil 'persp-merge-list frame-merge-list) + (frame-persp-order (reverse (persp--state-frame-v2-order frame)))) ;; iterate over the perspectives in the frame in the appropriate order (cl-loop for persp in frame-persp-order do (let ((state-single (gethash persp frame-persp-table))) -- cgit v1.0 From de57793d6e2c3396a91fcb3556e58e832eee2937 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Thu, 10 Mar 2022 16:38:51 -0500 Subject: Finally should have proper merge-list loading. --- perspective.el | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/perspective.el b/perspective.el index 02a44f5..42ed288 100644 --- a/perspective.el +++ b/perspective.el @@ -1908,15 +1908,15 @@ restored." (cl-loop for frame in (persp--state-complete-frames state-complete) do (cl-incf frame-count) (when (> frame-count 1) - (let ((new-frame (make-frame-command)) - (frame-merge-list (persp--state-frame-v2-merge-list frame))) - (set-frame-parameter new-frame 'merge-list frame-merge-list))) + (make-frame-command)) (let* ((frame-persp-table (persp--state-frame-v2-persps frame)) - (frame-persp-order (reverse (persp--state-frame-v2-order frame)))) + (frame-persp-order (reverse (persp--state-frame-v2-order frame))) + (frame-persp-merge-list (persp--state-frame-v2-merge-list frame))) ;; iterate over the perspectives in the frame in the appropriate order (cl-loop for persp in frame-persp-order do (let ((state-single (gethash persp frame-persp-table))) (persp-switch persp) + (set-frame-parameter nil 'persp-merge-list frame-persp-merge-list) (cl-loop for buffer in (persp--state-single-buffers state-single) do (persp-add-buffer buffer)) ;; XXX: split-window-horizontally is necessary for -- cgit v1.0 From 1d94436bd5ba25b079bfe2ccda8c59e799919a00 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Tue, 15 Mar 2022 12:36:53 -0400 Subject: Add persp-merge and persp-unmerge to perspective-map. --- perspective.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/perspective.el b/perspective.el index 42ed288..26abbef 100644 --- a/perspective.el +++ b/perspective.el @@ -326,6 +326,8 @@ Run with the activated perspective active.") (define-key perspective-map (kbd "p") 'persp-prev) (define-key perspective-map (kbd "") 'persp-prev) (define-key perspective-map persp-mode-prefix-key 'persp-switch-last) +(define-key perspective-map (kbd "m") 'persp-merge) +(define-key perspective-map (kbd "C-m") 'persp-unmerge) (define-key perspective-map (kbd "C-s") 'persp-state-save) (define-key perspective-map (kbd "C-l") 'persp-state-load) (define-key perspective-map (kbd "`") 'persp-switch-by-number) -- cgit v1.0 From e6637c4e9e193ffb924d29b6cb9acb75fa4fb87d Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Tue, 15 Mar 2022 12:55:06 -0400 Subject: Make persp-merge-list restorable regardless of frame count. --- perspective.el | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/perspective.el b/perspective.el index 26abbef..0d1dc5c 100644 --- a/perspective.el +++ b/perspective.el @@ -1881,7 +1881,7 @@ visible in a perspective as windows, they will be saved as FILE defaults to the value of persp-state-default-file if it is set. -Frames are restored, along with each frame's perspective list. +Frames are restored, along with each frame's perspective list and merge list. Each perspective's buffer list and window layout are also restored." (interactive (list @@ -1909,25 +1909,27 @@ restored." ;; iterate over the frames (cl-loop for frame in (persp--state-complete-frames state-complete) do (cl-incf frame-count) - (when (> frame-count 1) - (make-frame-command)) - (let* ((frame-persp-table (persp--state-frame-v2-persps frame)) - (frame-persp-order (reverse (persp--state-frame-v2-order frame))) - (frame-persp-merge-list (persp--state-frame-v2-merge-list frame))) - ;; iterate over the perspectives in the frame in the appropriate order - (cl-loop for persp in frame-persp-order do - (let ((state-single (gethash persp frame-persp-table))) - (persp-switch persp) - (set-frame-parameter nil 'persp-merge-list frame-persp-merge-list) - (cl-loop for buffer in (persp--state-single-buffers state-single) do - (persp-add-buffer buffer)) - ;; XXX: split-window-horizontally is necessary for - ;; window-state-put to succeed? Something goes haywire with root - ;; windows without it. - (split-window-horizontally) - (window-state-put (persp--state-single-windows state-single) - (frame-root-window (selected-frame)) - 'safe))))) + (let ((emacs-frame (if (> frame-count 1) (make-frame-command) (selected-frame))) + (frame-persp-table (persp--state-frame-v2-persps frame)) + (frame-persp-order (reverse (persp--state-frame-v2-order frame))) + (frame-persp-merge-list (persp--state-frame-v2-merge-list frame))) + (with-selected-frame emacs-frame + ;; restore the merge list + (set-frame-parameter emacs-frame 'persp-merge-list frame-persp-merge-list) + ;; iterate over the perspectives in the frame in the appropriate order + (cl-loop for persp in frame-persp-order do + (let ((state-single (gethash persp frame-persp-table))) + (persp-switch persp) + (set-frame-parameter nil 'persp-merge-list frame-persp-merge-list) + (cl-loop for buffer in (persp--state-single-buffers state-single) do + (persp-add-buffer buffer)) + ;; XXX: split-window-horizontally is necessary for + ;; window-state-put to succeed? Something goes haywire with root + ;; windows without it. + (split-window-horizontally) + (window-state-put (persp--state-single-windows state-single) + (frame-root-window emacs-frame) + 'safe)))))) ;; cleanup (persp-kill tmp-persp-name)) ;; after hook -- cgit v1.0