reddit / emacs

Combine ibuffer-projectile generated filters with other custom filters

rebelcan · 2022-02-20 04:11
Wanted to share something I've been working on: a way to combine the filters generated by the `ibuffer-projectile-generate-filter-groups` function in `ibuffer-projectile`! Basically, I wanted ibuffer & projectile to generate a list of projects on startup that I could combine with my own list of filters so that stuff inside a project gets grouped together, and all the stuff not in a project is grouped in ways that's useful to me instead of all just ending up under **Default**. Oh, and before I share the code I should say that I am by no means a lisp or elisp expert; this is just the result of an afternoon's worth of hacking on lisp to try and get it to do what I want. Alrighty, so the first thing I did was create my own version of `ibuffer-projectile-generate-filter-groups` so that it takes an optional argument: the list of extra filters to add to the list of project filter groups. (defun sh/ibuffer-projectile-generate-filter-groups (&optional additional) "Create a set of ibuffer filter groups based on the projectile root dirs of buffers." (let ((roots (ibuffer-remove-duplicates (delq nil (mapcar 'ibuffer-projectile-root (buffer-list)))))) (let ((new-groups (mapcar (lambda (root) (cons (funcall ibuffer-projectile-group-name-function (car root) (cdr root)) `((projectile-root . ,root)))) roots))) (if additional (append new-groups additional) new-groups)))) Then I use it in my configuration like so: (setq my-ibuffer-filter '(("dired" (mode . dired-mode)) ("chat" (mode . erc-mode)) ("sql" (or (mode . sql-mode) (mode . sql-interactive-mode))) ("shell" (or (mode . eshell-mode) (mode . shell-mode) (mode . ssh-mode))) ("org" (or (mode . org-mode) (name . "^\\*Org Agenda\\*$") (derived-mode . org-mode))) ("lsp" (or (name . "^\\*lsp-log\\*$") (name . "^\\*gopls") (name . "^\\*ruby-ls") (name . "^\\*ts-ls"))) ("magit" (or (mode . magit-mode) (derived-mode . magit-mode))) ("help" (or (name . "^\\*Help\\*$") (name . "^\\*helpful"))) ("emacs" (or (mode . debugger-mode) (name . "^\\*scratch\\*$") (name . "^\\*Messages\\*$") (name . "^\\*dashboard\\*$") (name . "^\\*Async-native-compile-log\\*$") (name . "^\\*Flymake log\\*$") (name . "^\\*flycheck") (name . "^\\*straight-process\\*$")))) ibuffer-saved-filter-groups (list (append (list "default") (sh/ibuffer-projectile-generate-filter-groups my-ibuffer-filter)))) And blamo, I've got projectile groups & my own custom groups without having to switch back and forth! I know I could just have separate filter groups, or maintain a list of projectile projects by hand. That's actually what I used to do, with lines like this: ("Prj:keyboard" (projectile-root "keyboard" . "~/Code/C++/keyboard")) But keeping that list up-to-date was annoying. I had to remember to update the list of ibuffer filters when pruning old projects from projectile. Now ibuffer gets updated automagically when I update my list of projectile projects! I know there's definitely room for improvement; `(list (append (list "default")` is ugly but I wasn't sure of the best way to marshal the output of the function so that it's what ibuffer is expecting to find in `ibuffer-saved-filter-groups`.
a-c-d-w · 2022-02-22 04:55
Okay, this is great -- finally time for me to look into Emacs's project capabilities! Thank you :) As far as your `(list (append (list "default") ...))` problem, `append` creates a list by itself so you can remove the outer `list` call :)