Skip to content

Commit

Permalink
Add "bufferlo anywhere"
Browse files Browse the repository at this point in the history
  • Loading branch information
florommel committed Jan 5, 2024
1 parent aa01299 commit 1360e91
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 2 deletions.
23 changes: 22 additions & 1 deletion README.org
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This gives you separate buffer lists per frame and per (tab-bar) tab.
Bufferlo is a lightweight wrapper around Emacs's buffer-list frame
parameter. In contrast to similar solutions, it integrates seamlessly
with the standard frame and tab management facilities, including
undeletion of frame and tabs, tab duplication and moving, frame
undeletion of frames and tabs, tab duplication and moving, frame
cloning, and persisting sessions (via desktop.el).

A buffer is added to the local buffer list when it is displayed in the
Expand Down Expand Up @@ -236,3 +236,24 @@ Of course, you can also set an arbitrary buffer as the initial frame buffer:
(switch-to-buffer "<BUFFER_NAME>")))
(add-hook 'after-make-frame-functions #'my-set-initial-frame-buffer)
#+END_SRC


** Bufferlo Anywhere

"Bufferlo anywhere" is an optional feature that lets you have
bufferlo's frame/tab-local buffer list anywhere you like, i.e. in any
command with interactive buffer selection (via ~read-buffer~,
e.g., ~diff-buffers~, ~make-indirect-buffer~, ...) -- not just in the
switch-buffer facilities. You can configure which commands use
bufferlo's local list and which use the global list.

Enable ~bufferlo-anywhere-mode~ to use bufferlo's local buffer list by
default. Customize ~bufferlo-anywhere-filter~ and
~bufferlo-anywhere-filter-type~ to restrict the commands that use the
local list. With the command prefix ~bufferlo-anywhere-disable-prefix~,
you can temporarily disable ~bufferlo-anywhere-mode~ for the next
command.

Instead the minor mode, you can use the command prefix
~bufferlo-anywhere-enable-prefix~, which only temporarily enables
bufferlo's local buffer list for the next command.
136 changes: 135 additions & 1 deletion bufferlo.el
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
;;; bufferlo.el --- Manage frame/tab-local buffer lists -*- lexical-binding: t -*-

;; Copyright (C) 2021-2023 Free Software Foundation, Inc.
;; Copyright (C) 2021-2024 Free Software Foundation, Inc.

;; Author: Florian Rommel <[email protected]>
;; Maintainer: Florian Rommel <[email protected]>
Expand Down Expand Up @@ -134,6 +134,26 @@ You can set this to \"*scratch*\"."
If nil, the local scratch buffers' major mode is set to `initial-major-mode'."
:type 'function)

(defcustom bufferlo-anywhere-filter '(switch-to-buffer bufferlo-switch-to-buffer)
"The functions that use the local buffer list in `bufferlo-anywhere-mode'.
If `bufferlo-anywhere-filter-type' is set to `exclude', this is an exclude
filter (i.e., determines the functions that do not use the local buffer list).
If `bufferlo-anywhere-filter-type' is set to `include' (or any other value),
this is an include filter.
The value can either be a list of functions, or t (for all functions),
or a custom filter function that takes a function symbol as its argument and
returns whether the probed function should be filtered (non-nil) or
not-filtered (nil)."
:type '(choice (repeat :tag "Filter specific functions" function)
(const :tag "All functions" t)
(function :tag "Custom filter function")))

(defcustom bufferlo-anywhere-filter-type 'exclude
"Determines whether `bufferlo-anywhere-filter' is an include or exclude filter.
Set this to `include' or `exclude'."
:type '(radio (const :tag "Include filter" include)
(const :tag "Exclude filter" exclude)))

(defvar bufferlo--desktop-advice-active nil)
(defvar bufferlo--desktop-advice-active-force nil)

Expand Down Expand Up @@ -776,6 +796,120 @@ The parameters OTHER-WINDOW-P NOSELECT SHRINK are passed to `ibuffer'."
(ibuffer other-window-p name '((bufferlo-orphan-buffers . nil))
noselect shrink)))

(define-minor-mode bufferlo-anywhere-mode
"Frame/tab-local buffer lists anywhere you like.
Enables bufferlo's local buffer list for any function that interactively prompts
for buffers via `read-buffer'. By default this enables the local buffer list
for (almost) all functions. Customize `bufferlo-anywhere-filter' and
`bufferlo-anywhere-filter-type' to adapt the behavior.
This minor mode requires `bufferlo-mode' to be enabled.
You can use `bufferlo-anywhere-disable' to disable the local buffer list for
the next command, when the mode is enabled."
:global t
:require 'bufferlo
:init-value nil
:lighter nil
:keymap nil
(if bufferlo-anywhere-mode
(advice-add #'call-interactively :around #'bufferlo--interactive-advice)
(advice-remove #'call-interactively #'bufferlo--interactive-advice)))

(defvar bufferlo--anywhere-tmp-enabled nil)
(defvar bufferlo--anywhere-tmp-disabled nil)
(defvar bufferlo--anywhere-old-read-buffer-function nil)
(defvar bufferlo--anywhere-nested nil)

(defun bufferlo--interactive-advice (oldfn function &optional record-flags keys)
"Advice function for `call-interactively' for `bufferlo-anywhere-mode'.
Temporarily overrides the `read-buffer-function' to filter the
available buffers to bufferlo's local buffer list.
OLDFN is the original function.
FUNCTION is the interactively called functions.
RECORD-FLAGS and KEYS are passed to `call-interactively'."
(if (or bufferlo--anywhere-tmp-enabled
(and (not bufferlo--anywhere-tmp-disabled)
(xor (eq bufferlo-anywhere-filter-type 'exclude)
(cond
((eq bufferlo-anywhere-filter t) t)
((listp bufferlo-anywhere-filter)
(memq function bufferlo-anywhere-filter))
((functionp bufferlo-anywhere-filter)
(funcall bufferlo-anywhere-filter function))))))
(let* ((bufferlo--anywhere-old-read-buffer-function
;; Preserve the original `read-buffer-function' but not for
;; nested calls; otherwise we would save our own function.
(if bufferlo--anywhere-nested
bufferlo--anywhere-old-read-buffer-function
read-buffer-function))
(bufferlo--anywhere-nested t)
(read-buffer-function
(lambda (prompt &optional def require-match predicate)
(let ((read-buffer-function
bufferlo--anywhere-old-read-buffer-function))
(read-buffer prompt def require-match
(lambda (b)
(and (bufferlo-local-buffer-p
(get-buffer
(if (stringp b) b (car b))))
(or (not predicate)
(funcall predicate b)))))))))
(apply oldfn (list function record-flags keys)))
;; `call-interactively' can be nested, e.g., on M-x invocations.
;; Therefore, we restore the original value of the `read-buffer-function'
;; if we do not use bufferlo's local buffer list for this call.
(let ((read-buffer-function
(if bufferlo--anywhere-nested
bufferlo--anywhere-old-read-buffer-function
read-buffer-function)))
(apply oldfn (list function record-flags keys)))))

(defun bufferlo-anywhere-disable-prefix ()
"Disable `bufferlo-anywhere-mode' only for the next command.
Has no effect if `bufferlo-anywhere-mode' is not enabled.
Has no effect if the next command does not query for a buffer."
(interactive)
(let* ((command this-command)
(minibuffer-depth (minibuffer-depth))
(postfun (make-symbol "bufferlo--anywhere-reenable-next-command")))
(fset postfun
(lambda ()
(unless (or
;; from window.el:display-buffer-override-next-command
(> (minibuffer-depth) minibuffer-depth)
(eq this-command command))
(setq bufferlo--anywhere-tmp-disabled nil)
(remove-hook 'post-command-hook postfun))))
(setq bufferlo--anywhere-tmp-disabled t)
(add-hook 'post-command-hook postfun)))

(defun bufferlo-anywhere-enable-prefix ()
"Use bufferlo's local buffer list for the next command.
Has a similar effect as `bufferlo-anywhere-mode' but only for the next command.
Has no effect if the next command does not query for a buffer.
Can be used with or without `bufferlo-anywhere-mode' enabled.
In contrast to `bufferlo-anywhere-mode', this does not adhere to
`bufferlo-anywhere-filter'. Therefore, you can use it in conjunction with
`bufferlo-anywhere-mode' to temporarily disable the configured filters."
(interactive)
(let* ((command this-command)
(minibuffer-depth (minibuffer-depth))
(postfun (make-symbol "bufferlo--anywhere-disable-next-command")))
(fset postfun
(lambda ()
(unless (or
;; from window.el:display-buffer-override-next-command
(> (minibuffer-depth) minibuffer-depth)
(eq this-command command))
(setq bufferlo--anywhere-tmp-enabled nil)
(unless bufferlo-anywhere-mode
(advice-remove #'call-interactively
#'bufferlo--interactive-advice))
(remove-hook 'post-command-hook postfun))))
(setq bufferlo--anywhere-tmp-enabled t)
(unless bufferlo-anywhere-mode
(advice-add #'call-interactively :around #'bufferlo--interactive-advice))
(add-hook 'post-command-hook postfun)))

(provide 'bufferlo)

;;; bufferlo.el ends here

0 comments on commit 1360e91

Please sign in to comment.