Skip to content

Commit

Permalink
Add/Change: Buffer filter functions and options
Browse files Browse the repository at this point in the history
  • Loading branch information
alphapapa committed Dec 16, 2020
1 parent 7d9dd75 commit 62d64c9
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 48 deletions.
1 change: 1 addition & 0 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ Of course, Ibuffer is a mature tool with many features, so Bufler doesn't replac
+ Show an asterisk next to buffers with unsaved changes. (Thanks to [[https://github.com/ration][Tatu Lahtela]].)
+ Option ~bufler-name-column-width~ limits the width of buffer names in the buffer list. (Thanks to [[https://github.com/WorldsEndless][Tory S. Anderson]].)
+ Column ~Mode~ shows buffer's major mode, sans ~-mode~ suffix.
+ More filtering options: ~bufler-filter-buffer-fns~, ~bufler-workspace-switch-buffer-filter-fns~, ~bufler-filter-buffer-modes~, and ~bufler-filter-buffer-name-regexps~. By default, more buffers will be hidden in ~bufler-list~ and ~bufler-switch-buffer~, and filters may be disabled by calling those commands with universal prefix arguments.

** 0.2

Expand Down
27 changes: 23 additions & 4 deletions bufler-workspace.el
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ May be customized to, e.g. only return the last element of a path."
(car (last (bufler-faceify-path path)))))
(function :tag "Custom function")))

(defcustom bufler-workspace-switch-buffer-filter-fns
'(bufler--buffer-hidden-p bufler--buffer-mode-filtered-p bufler--buffer-name-filtered-p)
"Buffers that match these functions are not shown when offering buffers for switching."
:type '(repeat
(choice (function-item bufler--buffer-hidden-p)
(function-item bufler--buffer-mode-filtered-p)
(function-item bufler--buffer-name-filtered-p)
(function-item bufler--buffer-special-p)
(function :tag "Custom function"))))

;;;; Commands

;;;###autoload
Expand Down Expand Up @@ -104,22 +114,31 @@ Interactively, use current buffer."
(bufler-workspace-frame-set (bufler-buffer-workspace-path buffer)))

;;;###autoload
(defun bufler-workspace-switch-buffer (&optional all-p set-workspace-p)
(defun bufler-workspace-switch-buffer (&optional all-p set-workspace-p no-filter)
"Switch to another buffer in the current group.
Without any input, switch to the previous buffer, like
`switch-to-buffer'. If ALL-P (interactively, with universal
prefix) or if the frame has no workspace, select from all
buffers. If SET-WORKSPACE-P (with two universal prefixes),
select from all buffers and set the frame's workspace.
select from all buffers and set the frame's workspace. If
NO-FILTER (with three universal prefixes), include buffers that
would otherwise be filtered by
`bufler-workspace-switch-buffer-filter-fns'.
If `bufler-workspace-switch-buffer-sets-workspace' is non-nil,
act as if SET-WORKSPACE-P is non-nil."
(interactive (list current-prefix-arg (equal '(16) current-prefix-arg)))
(interactive (list current-prefix-arg
(and current-prefix-arg
(>= (car current-prefix-arg) 16))
(and current-prefix-arg
(>= (car current-prefix-arg) 64))))
(let* ((bufler-vc-state nil)
(completion-ignore-case bufler-workspace-ignore-case)
(path (unless all-p
(frame-parameter nil 'bufler-workspace-path)))
(buffers (bufler-buffer-alist-at path))
(buffers (bufler-buffer-alist-at
path :filter-fns (unless no-filter
bufler-workspace-switch-buffer-filter-fns)))
(other-buffer-path (bufler-group-tree-leaf-path
(bufler-buffers) (other-buffer (current-buffer))))
(other-buffer-cons (cons (buffer-name (-last-item other-buffer-path))
Expand Down
115 changes: 77 additions & 38 deletions bufler.el
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,33 @@ might be slow, because `vc-registered' and `vc-refresh-state'
must be called to get up-to-date results."
:type 'boolean)

(defcustom bufler-filter-fns (list #'bufler-hidden-buffer-p)
"Buffers that match these functions are not shown."
:type '(repeat function))
(defcustom bufler-filter-buffer-modes
'(bufler-list-mode calendar-mode fundamental-mode helm-major-mode
magit-diff-mode magit-process-mode magit-revision-mode magit-section-mode
special-mode timer-list-mode)
"List of major modes whose buffers are not shown by default."
:type '(repeat string))

(defcustom bufler-filter-buffer-name-regexps
(list (rx "*Compile-Log*") (rx "*Disabled Command*")
;; Org export logs.
(rx "*Org " (1+ anything) "Output*"))
"Regular expressions matched against buffer names.
Buffers whose names match are hidden when function
`bufler--buffer-name-filtered-p' is in `bufler-filter-buffer-fns'
or `bufler-workspace-switch-buffer-filter-fns'."
:type '(repeat string))

(defcustom bufler-filter-buffer-fns
'(bufler--buffer-hidden-p bufler--buffer-mode-filtered-p
bufler--buffer-name-filtered-p)
"Buffers that match these functions are not shown by default."
:type '(repeat
(choice (function-item bufler--buffer-hidden-p)
(function-item bufler--buffer-mode-filtered-p)
(function-item bufler--buffer-name-filtered-p)
(function-item bufler--buffer-special-p)
(function :tag "Custom function"))))

(defcustom bufler-group-path-separator " » "
"Separator shown between path elements."
Expand Down Expand Up @@ -256,13 +280,14 @@ cleared with a timer that runs this many seconds after the last
type path elements)

;;;###autoload
(defun bufler-list (&optional force-refresh)
(defun bufler-list (&optional arg)
"Show Bufler's list.
With prefix argument FORCE-REFRESH, force refreshing of buffers'
VC state, and clear `bufler-cache' and regenerate buffer
groups (which can be useful after changing `bufler-groups' if the
buffer list has not yet changed)."
(interactive "P")
With prefix argument ARG, force refreshing of buffers' VC state,
clear `bufler-cache', and regenerate buffer groups (which can be
useful after changing `bufler-groups' if the buffer list has not
yet changed). With two universal prefix args, also show buffers
which are otherwise filtered by `bufler-filter-buffer-fns'."
(interactive "p")
(let (format-table)
(cl-labels
;; This gets a little hairy because we have to wrap `-group-by'
Expand Down Expand Up @@ -331,14 +356,13 @@ buffer list has not yet changed)."
(string arg)
(otherwise (format "%s" arg))))
(format< (test-dir buffer-dir)
(string< (as-string test-dir) (as-string buffer-dir)))
(boring-p (buffer)
(hidden-p buffer)))
(when force-refresh
(string< (as-string test-dir) (as-string buffer-dir))))
(when arg
(setf bufler-cache nil))
(pcase-let* ((inhibit-read-only t)
(bufler-vc-refresh force-refresh)
(groups (bufler-buffers))
(bufler-vc-refresh arg)
(groups (bufler-buffers :filter-fns (unless (>= arg 16)
bufler-filter-buffer-fns)))
(`(,*format-table . ,column-sizes) (bufler-format-buffer-groups groups))
(header (concat (format (format " %%-%ss" (cdar column-sizes)) (caar column-sizes))
(cl-loop for (name . size) in (cdr column-sizes)
Expand Down Expand Up @@ -495,28 +519,36 @@ NAME, okay, `checkdoc'?"

;;;; Functions

(cl-defun bufler-buffers (&key (groups bufler-groups) path)
(cl-defun bufler-buffers (&key (groups bufler-groups) filter-fns path)
"Return buffers grouped by GROUPS.
If PATH, return only buffers from the group at PATH."
If PATH, return only buffers from the group at PATH. If
FILTER-FNS, remove buffers that match any of them."
(cl-flet ((buffers
() (bufler-group-tree groups
(cl-loop with buffers = (cl-delete-if-not #'buffer-live-p (buffer-list))
for fn in bufler-filter-fns
do (setf buffers (cl-remove-if fn buffers))
finally return buffers))))
(if filter-fns
(cl-loop with buffers = (cl-delete-if-not #'buffer-live-p (buffer-list))
for fn in filter-fns
do (setf buffers (cl-remove-if fn buffers))
finally return buffers)
(buffer-list)))))
(let ((buffers (if bufler-use-cache
(let ((hash (sxhash (buffer-list))))
(if (equal hash (car bufler-cache))
(cdr bufler-cache)
(cdr (setf bufler-cache (cons hash (buffers))))))
(let ((key (sxhash (buffer-list))))
(if (eq key (car bufler-cache))
;; Buffer list unchanged.
(or (map-elt (cdr bufler-cache) filter-fns)
;; Different filters.
(setf (map-elt (cdr bufler-cache) filter-fns) (buffers)))
;; Buffer list has changed.
(cddr (setf bufler-cache (cons key (cons filter-fns (buffers)))))))
(buffers))))
(if path
(bufler-group-tree-at path buffers)
buffers))))

(cl-defun bufler-buffer-alist-at (path)
(cl-defun bufler-buffer-alist-at (path &key filter-fns)
"Return alist of (display . buffer) cells at PATH.
Each cell is suitable for completion functions."
Each cell is suitable for completion functions. If FILTER-FNS,
omit buffers that match any of them."
(interactive "P")
(let* ((level-start (pcase path
;; I don't like this, but it works for now. It's necessary because a group
Expand All @@ -526,7 +558,7 @@ Each cell is suitable for completion functions."
;; and some of the logic should probably be moved to bufler-group-tree.
('nil 0)
(_ (1+ (length (-take-while #'null path))))))
(grouped-buffers (bufler-buffers :path path))
(grouped-buffers (bufler-buffers :path path :filter-fns filter-fns))
(paths (bufler-group-tree-paths grouped-buffers)))
(cl-labels ((format-heading
(heading level) (propertize heading
Expand Down Expand Up @@ -579,7 +611,7 @@ Each cell is suitable for completion functions."
(buffer-modified-p buffer))
"*" "")
'face 'font-lock-warning-face))
(buffer-face (if (bufler-special-buffer-p buffer)
(buffer-face (if (bufler--buffer-special-p buffer)
'bufler-buffer-special 'bufler-buffer))
(level-face (bufler-level-face depth))
(face (list :inherit (list buffer-face level-face)))
Expand Down Expand Up @@ -632,16 +664,23 @@ Each cell is suitable for completion functions."
;; These functions take a buffer as their sole argument. They may be
;; used in the grouping predicates defined later.

(defun bufler-special-buffer-p (buffer)
(defun bufler--buffer-hidden-p (buffer)
"Return non-nil if BUFFER's name is prefixed by a space."
(string-prefix-p " " (buffer-name buffer)))

(defun bufler--buffer-mode-filtered-p (buffer)
"Return non-nil if BUFFER's major mode is in `bufler-filter-buffer-modes'."
(member (buffer-local-value 'major-mode buffer) bufler-filter-buffer-modes))

(defun bufler--buffer-name-filtered-p (buffer)
"Return non-nil if BUFFER's major mode is in `bufler-filter-buffer-name-regexps'."
(cl-loop for regexp in bufler-filter-buffer-name-regexps
thereis (string-match regexp (buffer-name buffer))))

(defun bufler--buffer-special-p (buffer)
"Return non-nil if BUFFER is special.
That is, if its name starts with \"*\"."
(string-match-p (rx bos (optional (1+ blank)) "*")
(buffer-name buffer)))

(defun bufler-hidden-buffer-p (buffer)
"Return non-nil if BUFFER is hidden.
That is, if its name starts with \" \"."
(string-match-p (rx bos (1+ blank)) (buffer-name buffer)))
(string-prefix-p "*" (buffer-name buffer)))

;;;;; Buffer and Column Formatting

Expand Down Expand Up @@ -991,7 +1030,7 @@ NAME, okay, `checkdoc'?"
"*indirect*"))

(bufler-defauto-group special
(if (bufler-special-buffer-p buffer)
(if (bufler--buffer-special-p buffer)
"*special*"
"non-special buffers"))

Expand Down
17 changes: 12 additions & 5 deletions bufler.info
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,13 @@ File: README.info, Node: 03-pre, Next: 02, Up: Changelog
in the buffer list. (Thanks to Tory S. Anderson
(https://github.com/WorldsEndless).)
• Column ‘Mode’ shows buffer’s major mode, sans ‘-mode’ suffix.
• More filtering options: ‘bufler-filter-buffer-fns’,
‘bufler-workspace-switch-buffer-filter-fns’,
‘bufler-filter-buffer-modes’, and
‘bufler-filter-buffer-name-regexps’. By default, more buffers will
be hidden in ‘bufler-list’ and ‘bufler-switch-buffer’, and filters
may be disabled by calling those commands with universal prefix
arguments.


File: README.info, Node: 02, Next: 01, Prev: 03-pre, Up: Changelog
Expand Down Expand Up @@ -613,11 +620,11 @@ Node: Prism support18181
Node: Compared to Ibuffer18621
Node: Changelog20371
Node: 03-pre20540
Node: 0221538
Node: 0121683
Node: Credits21782
Node: Development22471
Node: License22641
Node: 0221940
Node: 0122085
Node: Credits22184
Node: Development22873
Node: License23043

End Tag Table

Expand Down
6 changes: 5 additions & 1 deletion helm-bufler.el
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ This mimics `bufler-workspace-switch-buffer'."
(if (car (frame-parameter nil 'bufler-workspace-path))
(frame-parameter nil 'bufler-workspace-path)
(cdr (frame-parameter nil 'bufler-workspace-path))))))
(bufler-buffer-alist-at group-path)))
(pcase current-prefix-arg
((or `nil '(4) '(16))
(bufler-buffer-alist-at
group-path :filter-fns bufler-workspace-switch-buffer-filter-fns))
(_ (bufler-buffer-alist-at nil)))))
:action (cons (cons "Switch to buffer with Bufler" 'helm-bufler-switch-buffer)
helm-type-buffer-actions))
"Helm source for `bufler'.")
Expand Down

0 comments on commit 62d64c9

Please sign in to comment.