emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

outline-minor-mode for tabulated-list-mode


From: Juri Linkov
Subject: outline-minor-mode for tabulated-list-mode
Date: Sun, 18 Feb 2024 19:05:31 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/30.0.50 (x86_64-pc-linux-gnu)

Would it be nice to support outline-minor-mode in all modes
that are based on tabulated-list-mode such as list-packages,
list-processes, list-buffers, etc.

Here is how this looks for the list of buffers grouped by mode:

PNG image

The customization that groups by mode is simply:

  (setq Buffer-menu-group-by
        (lambda (b) (concat "* " (aref (cadr b) 5))))

Also note that sorting (e.g. by Size as above) sorts buffers
inside each group separately.

Another example is grouping by project name/root:

  (setq Buffer-menu-group-by
        (lambda (b)
          (with-current-buffer (car b)
            (if-let ((project (project-current)))
                (concat "* " (project-name project)) ;; or project-root
              "* Unorganized"))))

There are infinitely many ways to group buffers, so no predefined
functions are included.

Here is the minimal patch that implements this feature:

diff --git a/lisp/buff-menu.el b/lisp/buff-menu.el
index e13c3b56b4e..9123d9868f9 100644
--- a/lisp/buff-menu.el
+++ b/lisp/buff-menu.el
@@ -95,6 +95,12 @@ Buffer-menu-use-frame-buffer-list
   :group 'Buffer-menu
   :version "22.1")
 
+(defcustom Buffer-menu-group-by nil
+  "If non-nil, buffers are grouped by function."
+  :type 'function
+  :group 'Buffer-menu
+  :version "30.1")
+
 (defvar-local Buffer-menu-files-only nil
   "Non-nil if the current Buffer Menu lists only file buffers.
 This is set by the prefix argument to `buffer-menu' and related
@@ -674,7 +680,12 @@ list-buffers-noselect
       (setq Buffer-menu-buffer-list buffer-list)
       (setq Buffer-menu-filter-predicate filter-predicate)
       (list-buffers--refresh buffer-list old-buffer)
-      (tabulated-list-print))
+      (tabulated-list-print)
+      (when tabulated-list-groups
+        (setq-local outline-minor-mode-cycle t
+                    outline-minor-mode-highlight t
+                    outline-minor-mode-use-buttons 'in-margins)
+        (outline-minor-mode 1)))
     buffer))
 
 (defun Buffer-menu-mouse-select (event)
@@ -750,7 +761,11 @@ list-buffers--refresh
                  `("Mode" ,Buffer-menu-mode-width t)
                  '("File" 1 t)))
     (setq tabulated-list-use-header-line Buffer-menu-use-header-line)
-    (setq tabulated-list-entries (nreverse entries)))
+    (setq tabulated-list-entries (nreverse entries))
+    (when Buffer-menu-group-by
+      (setq tabulated-list-groups
+            (seq-group-by Buffer-menu-group-by
+                          tabulated-list-entries))))
   (tabulated-list-init-header))
 
 (defun tabulated-list-entry-size-> (entry1 entry2)
diff --git a/lisp/emacs-lisp/tabulated-list.el 
b/lisp/emacs-lisp/tabulated-list.el
index 9884a2fc24b..5b91670f8e9 100644
--- a/lisp/emacs-lisp/tabulated-list.el
+++ b/lisp/emacs-lisp/tabulated-list.el
@@ -139,6 +139,10 @@ tabulated-list-entries
 arguments and must return a list of the above form.")
 (put 'tabulated-list-entries 'permanent-local t)
 
+(defvar-local tabulated-list-groups nil
+  "Groups displayed in the current Tabulated List buffer.")
+(put 'tabulated-list-groups 'permanent-local t)
+
 (defvar-local tabulated-list-padding 0
   "Number of characters preceding each Tabulated List mode entry.
 By default, lines are padded with spaces, but you can use the
@@ -437,6 +441,9 @@ tabulated-list-print
 `tabulated-list-put-tag').  Don't use this immediately after
 changing `tabulated-list-sort-key'."
   (let ((inhibit-read-only t)
+        (groups (if (functionp tabulated-list-groups)
+                   (funcall tabulated-list-groups)
+                 tabulated-list-groups))
        (entries (if (functionp tabulated-list-entries)
                     (funcall tabulated-list-entries)
                   tabulated-list-entries))
@@ -447,7 +454,14 @@ tabulated-list-print
         (setq saved-col (current-column)))
     ;; Sort the entries, if necessary.
     (when sorter
-      (setq entries (sort entries sorter)))
+      (if groups
+          (setq groups
+                (mapcar (lambda (group)
+                          (cons (car group) (sort (cdr group) sorter)))
+                        groups))
+        (setq entries (sort entries sorter))))
+    (unless (functionp tabulated-list-groups)
+      (setq tabulated-list-groups groups))
     (unless (functionp tabulated-list-entries)
       (setq tabulated-list-entries entries))
     ;; Without a sorter, we have no way to just update.
@@ -459,6 +473,21 @@ tabulated-list-print
       (unless tabulated-list-use-header-line
         (tabulated-list-print-fake-header)))
     ;; Finally, print the resulting list.
+    (if groups
+        (dolist (group groups)
+          (insert (car group) ?\n)
+          (let ((saved-pt-new (tabulated-list-print-entries (cdr group) sorter 
update entry-id)))
+            (when saved-pt-new (setq saved-pt saved-pt-new))))
+      (setq saved-pt (tabulated-list-print-entries entries sorter update 
entry-id)))
+    (set-buffer-modified-p nil)
+    ;; If REMEMBER-POS was specified, move to the "old" location.
+    (if saved-pt
+       (progn (goto-char saved-pt)
+              (move-to-column saved-col))
+      (goto-char (point-min)))))
+
+(defun tabulated-list-print-entries (entries sorter update entry-id)
+  (let (saved-pt)
     (while entries
       (let* ((elt (car entries))
              (tabulated-list--near-rows
@@ -497,12 +526,7 @@ tabulated-list-print
       (setq entries (cdr entries)))
     (when update
       (delete-region (point) (point-max)))
-    (set-buffer-modified-p nil)
-    ;; If REMEMBER-POS was specified, move to the "old" location.
-    (if saved-pt
-       (progn (goto-char saved-pt)
-              (move-to-column saved-col))
-      (goto-char (point-min)))))
+    saved-pt))
 
 (defun tabulated-list-print-entry (id cols)
   "Insert a Tabulated List entry at point.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]