diff options
| author | Matthew White <mehw.is.me@inventati.org> | 2021-08-02 16:13:10 +0000 |
|---|---|---|
| committer | Matthew White <mehw.is.me@inventati.org> | 2021-10-21 00:22:43 +0200 |
| commit | bf0fce1fc80e60daa9f625509b9fd267922332ef (patch) | |
| tree | a34eddd252a45791538e693c711c37e7fff09f8c | |
| parent | 8dd102d26ccc478a0f1b12a0136aa38e8fcffc88 (diff) | |
persp-maybe-kill-buffer: don't kill a perspective's last buffer
Prevent killing or removing the last buffer left in perspectives,
unless a perspective is killed, to avoid adding a buffer of other
perspectives to the perspective where the last buffer is removed.
| -rw-r--r-- | CHANGELOG.md | 5 | ||||
| -rw-r--r-- | perspective.el | 79 |
2 files changed, 80 insertions, 4 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c7a3ac..4d497da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,12 +40,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added +- `persp-maybe-kill-buffer`: designed as `kill-buffer-query-functions` hook to keep a perspective's last left buffer from being killed. - `persp-get-buffer-names`: get any perspective's list of live buffers. - `persp-get-buffers`: get any perspective's list of buffers. ### Changed +- `persp-mode`: add/remove `persp-maybe-kill-buffer` hook. +- `persp-kill`: switch `persp-maybe-kill-buffer` on/off to allow killing a perspective's last left buffer. +- `persp-set-buffer`: walk perspectives rather than using a while `persp-buffer-in-other-p` loop, since the former isn't prone to infinite loops. This is needed due to buffers kept in perspectives by `persp-maybe-kill-buffer` and `persp-remove-buffer` when a buffer is a perspective`s last left buffer. +- `persp-remove-buffer`: do not kill/remove a perspective's last left buffer. - `persp-remove-buffer`: when burying a buffer, walk windows rather than using a while loop, since the former isn't prone to infinite loops. - `make-persp`: document that executing BODY saves/restores the `current-buffer`. - `persp-set-buffer`: follow the coding style of `persp-add-buffer`. diff --git a/perspective.el b/perspective.el index 49ee938..28a812b 100644 --- a/perspective.el +++ b/perspective.el @@ -819,9 +819,11 @@ See also `persp-switch' and `persp-remove-buffer'." (if (not (buffer-live-p buffer)) (message "buffer %s doesn't exist" buffer-or-name) (persp-add-buffer buffer) - (cl-loop for other-persp = (persp-buffer-in-other-p buffer) - while other-persp - do (with-perspective (cdr other-persp) + ;; Do not use the combination "while `persp-buffer-in-other-p'", + ;; if the buffer is not removed from other perspectives, it will + ;; go into an infinite loop. + (cl-loop for other-persp in (remove (persp-current-name) (persp-all-names)) + do (with-perspective other-persp (persp-remove-buffer buffer)))))) (cl-defun persp-buffer-in-other-p (buffer) @@ -853,6 +855,62 @@ Prefers perspectives in the selected frame." (persp-switch (cdr other-persp))) (switch-to-buffer buffer))))) +(defun persp-maybe-kill-buffer () + "Don't kill a buffer if it's the only buffer in a perspective. + +This is the default behaviour of `kill-buffer'. Perspectives +with only one buffer should keep it alive to prevent adding a +buffer from another perspective, replacing the killed buffer. + +Will also cleanup killed buffers form each perspective's list +of buffers containing the buffer to be killed. + +This is a hook for `kill-buffer-query-functions'. Don't call +this directly, otherwise the current buffer may be removed or +killed from perspectives. + +See also `persp-remove-buffer'." + ;; List candidates where the buffer to be killed should be removed + ;; instead, whom are perspectives with more than one buffer. This + ;; is to allow the buffer to live for perspectives that have it as + ;; their only buffer. + (persp-protect + (let* ((buffer (current-buffer)) + (bufstr (buffer-name buffer)) + candidates-for-removal candidates-for-keeping) + (dolist (name (persp-names)) + (let ((buffer-names (persp-get-buffer-names name))) + (when (member bufstr buffer-names) + (if (cdr buffer-names) + (push name candidates-for-removal) + ;; We use a list for debugging purposes, a simple bool + ;; can suffice for what we are doing here. + (push name candidates-for-keeping))))) + (cond + ;; When there aren't perspectives with the buffer as the only + ;; buffer, it can be killed safely. Also cleanup killed ones + ;; found in perspectives listing the buffer to be killed. + ((not candidates-for-keeping) + ;; Switching to a perspective that isn't the current, should + ;; automatically cleanup previously killed buffers which are + ;; still in the perspective's list of buffers. Removing the + ;; buffer to be killed should also keep the list clean. + (dolist (name candidates-for-removal) + (with-perspective name + ;; remove the buffer that has to be killed from the list + (setf (persp-current-buffers) (remq buffer (persp-current-buffers))))) + t) + ;; When a perspective have the buffer as the only buffer, the + ;; buffer should not be killed, but removed from perspectives + ;; that have more than one buffer. To remove the buffer, all + ;; that's needed is `persp-remove-buffer' while the buffer is + ;; kept alive in at least one perspective. + (candidates-for-removal + (dolist (name candidates-for-removal) + (with-perspective name + (persp-remove-buffer buffer))) + nil))))) + (defun persp-remove-buffer (buffer) "Disassociate BUFFER with the current perspective. @@ -861,7 +919,16 @@ See also `persp-switch' and `persp-add-buffer'." (list (funcall persp-interactive-completion-function "Remove buffer from perspective: " (persp-current-buffer-names)))) (setq buffer (when buffer (get-buffer buffer))) (cond ((not (buffer-live-p buffer))) - ;; Only kill the buffer if no other perspectives are using it + ;; Do not kill or remove a buffer if the perspective will then + ;; switch to the buffer of another perspective. It may happen + ;; when the buffer is the perspective's last left buffer or if + ;; the next candidate is a perspective's special buffer. This + ;; could not be enforced when a perspective is killed. + ((and (persp-is-current-buffer buffer) + (memq 'persp-maybe-kill-buffer kill-buffer-query-functions) + (not (remove (buffer-name buffer) (persp-current-buffer-names)))) + (setq buffer nil)) + ;; Only kill the buffer if no other perspectives are using it. ((not (persp-buffer-in-other-p buffer)) (kill-buffer buffer)) ;; Make the buffer go away if we can see it. @@ -894,10 +961,12 @@ Killing a perspective means that all buffers associated with that perspective and no others are killed." (interactive "i") (if (null name) (setq name (persp-prompt (persp-current-name) t))) + (remove-hook 'kill-buffer-query-functions 'persp-maybe-kill-buffer) (with-perspective name (run-hooks 'persp-killed-hook) (mapc 'persp-remove-buffer (persp-current-buffers)) (setf (persp-killed (persp-curr)) t)) + (add-hook 'kill-buffer-query-functions 'persp-maybe-kill-buffer) (remhash name (perspectives-hash)) (when (boundp 'persp--xref-marker-ring) (remhash name persp--xref-marker-ring)) (persp-update-modestring) @@ -1165,6 +1234,7 @@ named collections of buffers and window configurations." (add-hook 'after-make-frame-functions 'persp-init-frame) (add-hook 'delete-frame-functions 'persp-delete-frame) (add-hook 'ido-make-buffer-list-hook 'persp-set-ido-buffers) + (add-hook 'kill-buffer-query-functions 'persp-maybe-kill-buffer) (setq read-buffer-function 'persp-read-buffer) (mapc 'persp-init-frame (frame-list)) (setf (persp-current-buffers) (buffer-list)) @@ -1174,6 +1244,7 @@ named collections of buffers and window configurations." (remove-hook 'delete-frame-functions 'persp-delete-frame) (remove-hook 'after-make-frame-functions 'persp-init-frame) (remove-hook 'ido-make-buffer-list-hook 'persp-set-ido-buffers) + (remove-hook 'kill-buffer-query-functions 'persp-maybe-kill-buffer) (setq read-buffer-function nil) (set-frame-parameter nil 'persp--hash nil) (setq global-mode-string (delete '(:eval (persp-mode-line)) global-mode-string)) |
