bufler syntax for a browser subgroup

Table of Contents

Edits

  • [2023-12-23 Sat] correction: “Buffler” is actually “Bufler”
  • [2023-12-22 Fri] added updated groups section, and linked to my Firefox usage post. Added comments sections.

Discussion

Discussion can be had on this post at

Github issue text

This is associated with a Github issue.1

Note that this is with exwm, so Firefox buffers are first-class buffer citizens2. I used to have a single Firefox tab which contained my various social tabs with their particular settings. I decided that, in order to live a more tab-free lifestyle and maximize the benefits of exwm, I would pull those out. Ideally I could off-load that collection to Bufler to handle. But, though my catch-all Firefox setup in bufler has worked for ages, my desire to seperate a collection of Firefox tabs into their own subgroup is failing. So, two questions: Is such subgrouping possible? And, if so, what syntax do I need?

Here is my failing try:

(group
 (name-match "*Firefox*" (rx bos "F:")) ;; this part has worked for a long time
 (group-and "Mastodon" ;; is something wrong with my syntax?
            (name-match (rx "Mastodon\|mstdn\|qoto"))))

Answer

Now my social windows are grouped together visibly under my Firefox section

Bufler author Alphapapa pointed out the simple syntax error with my use of rx, which I hadn’t realized is intended to convert sexp into regexp.

Well, you’re using rx, but you’ve got \| in the string, which defeats the purpose of using rx (and it will escape those metacharacters). The point of using rx is being able to write the regexp as a sexp, like (rx (or "Mastodon" "mstdn" ...)).

I didn’t realize that was the roll of rx and changed accordingly. With a little face change3 to make things a little more visible, here is my working code:

(setf bufler-groups
      (bufler-defgroups
      ;;; other groups...
        (group
         (name-match "*Firefox*" (rx bos "F:"))
         (group-and "Mastodon"
                    (name-match "Mastodon" (rx (or "Mastodon" "mstdn" "qoto"))))
         )
         ;;; more other
         )

Update: Fleshed-out Bufler Firefox groups

Bufler grouping of my persistent Firefox groups

[2023-12-22 Fri] I have refined my groups, better showing which groups I hav open, followed by a catch-all “web” buffer for everything else FireFox. I found that by using nested group instead of group-and or group-or it gave me the grouping I wanted, particularly putting a catch-all group that would have all the Firefox buffers that didn’t fit one of the other categories.

(setf bufler-groups
        (bufler-defgroups
          (group
           ;; Subgroup collecting all named workspaces.
           (auto-workspace))
          (group
           (mode-match "*w3m*" (rx "w3m")))
          (group
           (name-match "*Nightly Private Browsing" (rx "Private Browsing" eos)))
          (group
           (name-match "*Firefox*" (rx bos "F:"))
           (group
            (name-match "Federated" (rx
                                  (or "Mastodon"
                                      "mstdn"
                                      "qoto"
                                      "bookwyrm"
                                      "discord"))))
           (group
            (name-match "Media" (rx
                                 (or "spotify.com"
                                     "piped"
                                     "youtube.com"
                                     "twitch"))))
           (group
            (name-match "Comms" (rx
                                 (or "teams.microsoft"
                                     "discord.com"
                                     "zulipchat.com"))))
           (group
            (name-match "Web" ".*"))
           )

          (group (mode-match "*EXWM*" (rx bos "EXWM")))
          (group
           (group-or "Chat"
                     (mode-match "Telega" (rx bos "telega-"))))
          (group
           ;; Subgroup collecting all `help-mode' and `info-mode' buffers.
           (group-or "*Help/Info*"
                     (mode-match "*Help*" (rx bos "help-"))
                     (mode-match "*Info*" (rx bos "info-"))))
          (group
           ;; Subgroup collecting all special buffers (i.e. ones that are not
           ;; file-backed), except `magit-status-mode' buffers (which are allowed to fall
           ;; through to other groups, so they end up grouped with their project buffers).
           (group-and "*Special*"
                      (lambda (buffer)
                        (unless (or (funcall (mode-match "Magit" (rx bos "magit-status"))
                                             buffer)
                                    (funcall (mode-match "Dired" (rx bos "dired"))
                                             buffer)
                                    (funcall (auto-file) buffer))
                          "*Special*")))
           (group
            ;; Subgroup collecting these "special special" buffers
            ;; separately for convenience.
            (name-match "**Special**"
                        (rx bos "*" (or "Messages" "Warnings" "scratch" "Backtrace") "*")))
           (group
            ;; Subgroup collecting all other Magit buffers, grouped by directory.
            (mode-match "*Magit* (non-status)" (rx bos (or "magit" "forge") "-"))
            (auto-directory))
           ;; Remaining special buffers are grouped automatically by mode.
           (auto-mode))
          ;; All buffers under "~/.emacs.d" (or wherever it is).
          (dir user-emacs-directory)
          (group
           ;; Subgroup collecting buffers in `org-directory' (or "~/org" if
           ;; `org-directory' is not yet defined).
           (dir (if (bound-and-true-p org-directory)
                    org-directory
                  "~/org"))
           (group
            ;; Subgroup collecting indirect Org buffers, grouping them by file.
            ;; This is very useful when used with `org-tree-to-indirect-buffer'.
            (auto-indirect)
            (auto-file))
           ;; Group remaining buffers by whether they're file backed, then by mode.
           (group-not "*special*" (auto-file))
           (auto-mode))
          (group
           ;; Subgroup collecting buffers in a projectile project.
           (auto-projectile))
          (group
           ;; Subgroup collecting buffers in a version-control project,
           ;; grouping them by directory.
           (auto-project))
          ;; Group remaining buffers by directory, then major mode.
          (auto-directory)
          (auto-mode)))

Editing my Bufler setup for rapid feedback

As per the image above, it is easy and pleasant to update my bufler setup. I use a literate setup, so I manage my init file via orgmode. Then I have all my bufler settings in a single block, and I can just use C-c ' (org-edit-special) and get a dedicated buffer of that code snippet, in the editing mode of that language (such as lisp). Once in that dedicated buffer, I make any changes and run M-x eval-buffer and then switch to by open bufler listing, press g to refresh, and my updated grouping is available.

Footnotes

1 I have been enjoying AlphaPapa’s Bufler for buffer management for a while https://github.com/alphapapa/bufler.el/issues/93 . Although I use it very plainly, it has excellent support for perspectives, tab bar, etc.

2 You can checkout how I made my firefox mostly tabless, for heightened emacsing: https://tech.toryanderson.com/2021/04/06/firefox-address-in-titles-for-exwm/

3 The face to change was found by running helpful-at-point and seeing that it was git-section-face and then customizing until I found an acceptable color (saving in customizer would immediately update my bufler window, which was open at the same time), ending up with the following customization line (which, thanks to the customizer, I didn’t write; I just picked the color): (magit-section-highlight ((t (:extend t :background "dark blue"))))

Tory Anderson avatar
Tory Anderson
Full-time Web App Engineer, Digital Humanist, Researcher, Computer Psychologist