Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add frame global perspective. #178

Merged
merged 5 commits into from
Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ The actual command keys (the ones pressed after the prefix) are defined in
Since Perspective maintains distinct buffer lists for each perspective, it helps
to use a Perspective-aware buffer switcher.

When using one of the following buffer switchers you will only be prompted for
buffers in the current perspective and the frame global perspective.

**Ido**: [Interactive Do (Ido,
`ido-mode`)](https://www.gnu.org/software/emacs/manual/html_node/ido/index.html),
in particular its `ido-switch-buffer` command, is automatically
Expand Down
128 changes: 103 additions & 25 deletions perspective.el
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@ If 'created, then sort by time created (latest first)."
(const :tag "By Time Accessed" access)
(const :tag "By Time Created" created)))

(defcustom persp-frame-global-perspective-name "GLOBAL"
"The name for a frames global perspective."
:group 'perspective-mode
:type 'string)

(defcustom persp-frame-global-perspective-include-scratch-buffer nil
"If non-nil include `persp-frame-global-perspective-name's scratch buffer to
buffer switch options."
:group 'perspective-mode
:type 'boolean)

(defcustom persp-state-default-file nil
"When non-nil, it provides a default argument for `persp-state-save` and `persp-state-load` to work with.

Expand Down Expand Up @@ -171,31 +182,51 @@ After BODY is evaluated, frame parameters are reset to their original values."
;; return a regex which matches nothing, and therefore should ignore nothing
"$^"))

;; NOTE: This macro is used as a place for setf expressions so be careful with
;; how you modify it as you may break things in surprising ways.
(defmacro persp-current-buffers ()
"Return a list of all buffers in the current perspective."
`(persp-buffers (persp-curr)))

(defun persp-current-buffer-names ()
"Return a list of names of all living buffers in the current perspective."
(defun persp-current-buffers* (&optional include-global)
"Same as `persp-current-buffers' but if INCLUDE-GLOBAL include buffers from
the frame global perspective."
(if (not include-global)
(persp-current-buffers)
(delete-dups
(append (persp-current-buffers)
(when (member persp-frame-global-perspective-name (persp-names))
(with-perspective persp-frame-global-perspective-name
(if persp-frame-global-perspective-include-scratch-buffer
(persp-current-buffers)
(remove (persp-get-scratch-buffer) (persp-current-buffers)))))))))

(defun persp-current-buffer-names (&optional include-global)
"Return a list of names of all living buffers in the current perspective.
Include the names of the buffers in the frame global perspective when
INCLUDE-GLOBAL."
(let ((ignore-rx (persp--make-ignore-buffer-rx)))
(cl-loop for buf in (persp-current-buffers)
(cl-loop for buf in (persp-current-buffers* include-global)
if (and (buffer-live-p buf)
(not (string-match-p ignore-rx (buffer-name buf))))
collect (buffer-name buf))))

(defun persp-is-current-buffer (buf)
"Return T if BUF is in the current perspective."
(memq buf (persp-current-buffers)))
(defun persp-is-current-buffer (buf &optional include-global)
"Return T if BUF is in the current perspective. When INCLUDE-GLOBAL also
return T if BUF is in the frame global perspective."
(memq buf (persp-current-buffers* include-global)))

(defun persp-buffer-filter (buf)
"Return F if BUF is in the current perspective. Used for
filtering in buffer display modes like ibuffer."
(not (persp-is-current-buffer buf)))
(defun persp-buffer-filter (buf &optional include-global)
"Return F if BUF is in the current perspective. when INCLUDE-GLOBAL also
return F if BUF is in the frame global perspective. Used for filtering in buffer
display modes like ibuffer."
(not (persp-is-current-buffer buf include-global)))

(defun persp-buffer-list-filter (bufs)
"Return the subset of BUFS which is in the current perspective."
(defun persp-buffer-list-filter (bufs &optional include-global)
"Return the subset of BUFS which is in the current perspective. When
EXCLUDE-GLOBAL include buffers that are members of the frame global perspective."
(cl-loop for buf in bufs
if (persp-is-current-buffer (get-buffer buf))
if (persp-is-current-buffer (get-buffer buf) include-global)
collect buf))

(defun persp-valid-name-p (name)
Expand Down Expand Up @@ -328,6 +359,7 @@ Run with the activated perspective active.")
(define-key perspective-map persp-mode-prefix-key 'persp-switch-last)
(define-key perspective-map (kbd "m") 'persp-merge)
(define-key perspective-map (kbd "u") 'persp-unmerge)
(define-key perspective-map (kbd "g") 'persp-add-frame-global-buffer)
(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)
Expand Down Expand Up @@ -836,6 +868,17 @@ See also `persp-switch' and `persp-remove-buffer'."
(unless (persp-is-current-buffer buffer)
(push buffer (persp-current-buffers))))))

(defun persp-add-frame-global-buffer (buffer-or-name)
"Associate BUFFER-OR-NAME with the frame global perspective.

See also `persp-add-buffer'"
(interactive
(list
(let ((read-buffer-function nil))
(read-buffer "Add buffer to frame global perspective: "))))
(with-perspective persp-frame-global-perspective-name
(persp-add-buffer buffer-or-name)))

(defun persp-set-buffer (buffer-or-name)
"Associate BUFFER-OR-NAME with the current perspective and remove it from any other."
(interactive
Expand All @@ -853,6 +896,17 @@ See also `persp-switch' and `persp-remove-buffer'."
do (with-perspective other-persp
(persp-forget-buffer buffer))))))

(defun persp-set-frame-global-perspective (buffer-or-name)
"Associate BUFFER-OR-NAME with the frame global perspective and remove it from
any other.

See also `persp-set-buffer'."
(list
(let ((read-buffer-function nil))
(read-buffer "Set buffer to frame global perspective: ")))
(with-perspective persp-frame-global-perspective-name
(persp-set-buffer buffer-or-name)))

(cl-defun persp-buffer-in-other-p (buffer)
"Returns nil if BUFFER is only in the current perspective.
Otherwise, returns (FRAME . NAME), the frame and name of another
Expand Down Expand Up @@ -980,6 +1034,18 @@ See also `persp-add-buffer' and `persp-remove-buffer'."
(set-buffer (window-buffer))
(setf (persp-current-buffers) (remq buffer (persp-current-buffers))))

(defun persp-forget-frame-global-buffer (buffer)
"Disassociate BUFFER from the frame global perspective.
If BUFFER isn't in any perspective, then it is in limbo.

See also `persp-forget-buffer'."
(interactive
(list (funcall persp-interactive-completion-function "Disassociate buffer from frame global perspective: "
(with-perspective persp-frame-global-perspective-name
(persp-current-buffer-names)))))
(with-perspective persp-frame-global-perspective-name
(persp-forget-buffer buffer)))

(defun persp-remove-buffer (buffer)
"Remove BUFFER from the current perspective.
Kill BUFFER if it falls into limbo (not in any perspective).
Expand All @@ -1006,6 +1072,17 @@ See also `persp-switch' and `persp-add-buffer'."
;; Make the buffer go away if we can see it.
((persp-forget-buffer buffer))))

(defun persp-remove-frame-global-buffer (buffer)
"Remove BUFFER from the frame global perspective.

See also `persp-remove-buffer'."
(interactive
(list (funcall persp-interactive-completion-function "Remove buffer from frame global perspective: "
(with-perspective persp-frame-global-perspective-name
(persp-current-buffer-names)))))
(with-perspective persp-frame-global-perspective-name
(persp-remove-buffer buffer)))

(defun persp-kill (name)
"Kill the perspective given by NAME.

Expand Down Expand Up @@ -1379,7 +1456,7 @@ it. In addition, if one exists already, runs BODY in it immediately."
"Restrict the ido buffer to the current perspective."
(defvar ido-temp-list)
(let ((persp-names
(remq nil (mapcar 'buffer-name (persp-current-buffers))))
(remq nil (mapcar 'buffer-name (persp-current-buffers* t))))
(indices (make-hash-table :test 'equal)))
(cl-loop for elt in ido-temp-list
for i upfrom 0
Expand Down Expand Up @@ -1444,7 +1521,7 @@ PERSP-SET-IDO-BUFFERS)."
(if (or current-prefix-arg (not persp-mode))
(let ((read-buffer-function nil))
(read-buffer-to-switch "Switch to buffer"))
(let* ((candidates (persp-current-buffer-names))
(let* ((candidates (persp-current-buffer-names t))
(other (buffer-name (persp-other-buffer (current-buffer)))))
;; NB: This intentionally calls completing-read instead of
;; persp-interactive-completion-function, since it is expected to have
Expand Down Expand Up @@ -1494,7 +1571,7 @@ PERSP-SET-IDO-BUFFERS)."
(interactive "P")
(if (and persp-mode (null arg))
(switch-to-buffer
(list-buffers-noselect nil (seq-filter 'buffer-live-p (persp-current-buffers))))
(list-buffers-noselect nil (seq-filter 'buffer-live-p (persp-current-buffers* t))))
(switch-to-buffer (list-buffers-noselect))))

;; Buffer switching integration: list-buffers.
Expand All @@ -1504,7 +1581,7 @@ PERSP-SET-IDO-BUFFERS)."
(interactive "P")
(if (and persp-mode (null arg))
(display-buffer
(list-buffers-noselect nil (seq-filter 'buffer-live-p (persp-current-buffers))))
(list-buffers-noselect nil (seq-filter 'buffer-live-p (persp-current-buffers* t))))
(display-buffer (list-buffers-noselect))))

;; Buffer switching integration: bs.el.
Expand All @@ -1523,7 +1600,7 @@ PERSP-SET-IDO-BUFFERS)."
(let* ((ignore-rx (persp--make-ignore-buffer-rx))
(bs-configurations (append bs-configurations
(list `("perspective" nil nil
,ignore-rx persp-buffer-filter nil))
,ignore-rx (lambda (buf) (persp-buffer-filter buf t)) nil))
(list `("all-perspectives" nil nil
,ignore-rx nil nil)))))
(if (and persp-mode (null arg))
Expand All @@ -1545,7 +1622,7 @@ PERSP-SET-IDO-BUFFERS)."
(defvar ibuffer-maybe-show-predicates)
(if (and persp-mode (null arg))
(let ((ibuffer-maybe-show-predicates (append ibuffer-maybe-show-predicates
(list #'persp-buffer-filter)
(list #'(lambda (buf) (persp-buffer-filter buf t)))
ido-ignore-buffers)))
(ibuffer))
(ibuffer)))
Expand All @@ -1564,8 +1641,8 @@ PERSP-SET-IDO-BUFFERS)."
:default t
:items
#'(lambda () (consult--buffer-query :sort 'visibility
:predicate 'persp-is-current-buffer
:as #'buffer-name)))))
:predicate '(lambda (buf) (persp-is-current-buffer buf t))
:as #'buffer-name)))))

;; Buffer switching integration: Ivy.
;;
Expand All @@ -1575,7 +1652,7 @@ PERSP-SET-IDO-BUFFERS)."
;; (defun persp-ivy-read-advice (args)
;; (append args
;; (list :predicate
;; (lambda (b) (persp-is-current-buffer (cdr b))))))
;; (lambda (b) (persp-is-current-buffer (cdr b) t)))))
;; (advice-add 'ivy-read :filter-args #'persp-ivy-read-advice)
;; (advice-remove 'ivy-read #'persp-ivy-read-advice)

Expand All @@ -1584,14 +1661,15 @@ PERSP-SET-IDO-BUFFERS)."
(user-error "Ivy not loaded"))
(declare-function ivy-read "ivy.el")
(if (and persp-mode (null arg))
(let ((real-ivy-read (symbol-function 'ivy-read)))
(let ((real-ivy-read (symbol-function 'ivy-read))
(current-bufs (persp-current-buffers* t)))
(cl-letf (((symbol-function 'ivy-read)
(lambda (&rest args)
(apply real-ivy-read
(append args
(list :predicate
(lambda (b)
(persp-is-current-buffer (cdr b)))))))))
(memq (cdr b) current-bufs))))))))
(funcall fallback)))
(funcall fallback)))

Expand All @@ -1617,7 +1695,7 @@ PERSP-SET-IDO-BUFFERS)."
(defun persp--helm-buffer-list-filter (bufs)
(if current-prefix-arg
bufs
(persp-buffer-list-filter bufs)))
(persp-buffer-list-filter bufs t)))

(defun persp--helm-remove-buffers-from-perspective (_arg)
(interactive)
Expand Down