aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--perspective.el12
-rw-r--r--test/test-perspective.el21
3 files changed, 32 insertions, 2 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b90c9af..90255d9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Temporary `switch-to-buffer` displays with non-nil `norecord` no longer add the displayed buffer to the current perspective. This fixes `consult-buffer` preview importing previewed buffers ([#225](https://github.com/nex3/perspective-el/issues/225)).
- `persp-maybe-kill-buffer`: improve performance by avoiding switching perspectives and rebuilding buffer-name lists ([#226](https://github.com/nex3/perspective-el/issues/226)).
+- `persp-delete-frame`: guard against reentrant frame-deletion cleanup to avoid excessive Lisp nesting when killing frames with dedicated windows ([#195](https://github.com/nex3/perspective-el/issues/195)).
### Added
diff --git a/perspective.el b/perspective.el
index 962a500..1d4191c 100644
--- a/perspective.el
+++ b/perspective.el
@@ -404,6 +404,12 @@ Run with the activated perspective active.")
(defvar persp--winner-after-load-registered nil
"Non-nil when Winner setup has been registered via `eval-after-load'.")
+(defvar persp--delete-frame-in-progress nil
+ "Non-nil while `persp-delete-frame' is cleaning up a frame.
+This prevents reentrant calls when killing a perspective causes a
+buffer kill, which may trigger another `delete-frame' while the
+original frame teardown is still in progress.")
+
(defvar persp-mode-map (make-sparse-keymap)
"Keymap for perspective-mode.")
@@ -1551,8 +1557,10 @@ By default, this uses the current frame."
"Clean up perspectives in FRAME.
By default this uses the current frame."
(with-selected-frame frame
- (unless persp-started-after-server-mode
- (mapcar #'persp-kill (persp-names)))))
+ (unless (or persp-started-after-server-mode
+ persp--delete-frame-in-progress)
+ (let ((persp--delete-frame-in-progress t))
+ (mapc #'persp-kill (persp-names))))))
(defun persp-make-variable-persp-local (variable)
"Make VARIABLE become perspective-local.
diff --git a/test/test-perspective.el b/test/test-perspective.el
index c4f1331..31f3272 100644
--- a/test/test-perspective.el
+++ b/test/test-perspective.el
@@ -2427,4 +2427,25 @@ persp-test-make-sample-environment."
(sort (persp-get-buffer-names "C") #'string-lessp))))))))
(persp-test-clean-files "A1" "A2" "B1" "B2" "C1" "C2"))))
+(ert-deftest basic-persp-delete-frame-ignores-reentrant-calls ()
+ "Test that `persp-delete-frame' ignores nested reentrant calls."
+ (persp-test-with-persp
+ (persp-switch "A")
+ (persp-switch "B")
+ (let ((expected-names (sort (copy-sequence (persp-names)) #'string-lessp))
+ killed-names
+ reentered)
+ (cl-letf (((symbol-function 'persp-kill)
+ (lambda (name)
+ (push name killed-names)
+ ;; Simulate a nested `delete-frame' triggered while
+ ;; perspective cleanup is already in progress.
+ (unless reentered
+ (setq reentered t)
+ (persp-delete-frame (selected-frame))))))
+ (persp-delete-frame (selected-frame)))
+ (should reentered)
+ (should (equal expected-names
+ (sort killed-names #'string-lessp))))))
+
;;; test-perspective.el ends here