├── README.md
└── ibuffer-projectile.el
/README.md:
--------------------------------------------------------------------------------
1 | [](http://melpa.org/#/ibuffer-projectile)
2 | [](http://stable.melpa.org/#/ibuffer-projectile)
3 |
4 |
5 | # ibuffer-projectile: Group buffers in ibuffer list by projectile project #
6 |
7 | Emacs' `ibuffer-mode` is a wonderful replacement for the built-in
8 | `list-buffer` command, and allows buffers to be grouped
9 | programatically, e.g. by major mode.
10 |
11 | `ibuffer-projectile` lets you group your buffers by their projectile
12 | root directory.
13 |
14 | You can use this package manually or automatically. For manual use,
15 | call `ibuffer-projectile-set-filter-groups`. To have this function
16 | called when you open ibuffer, add this hook to your configuration:
17 |
18 | ```el
19 | (add-hook 'ibuffer-hook
20 | (lambda ()
21 | (ibuffer-projectile-set-filter-groups)
22 | (unless (eq ibuffer-sorting-mode 'alphabetic)
23 | (ibuffer-do-sort-by-alphabetic))))
24 | ```
25 |
26 | Alternatively, use `ibuffer-projectile-generate-filter-groups'
27 | to programmatically obtain a list of filter groups that you can
28 | combine with your own custom groups.
29 |
30 | To display filenames relative to the project root, use project-relative-file
31 | in `ibuffer-formats`, e.g.:
32 |
33 | ```el
34 | (setq ibuffer-formats
35 | '((mark modified read-only " "
36 | (name 18 18 :left :elide)
37 | " "
38 | (size 9 -1 :right)
39 | " "
40 | (mode 16 16 :left :elide)
41 | " "
42 | project-relative-file)))
43 | ```
44 |
45 | I personally use [ibuffer-vc](https://github.com/purcell/ibuffer-vc)
46 | because I prefer its grouping behaviour, but I thought this would be
47 | useful to some people too.
48 |
49 | ## How to install ##
50 |
51 | Add `ibuffer-projectile.el` to your `load-path`, or (preferred) install from [Melpa][Melpa].
52 |
53 |
54 | [Melpa]: http://melpa.org "Melpa"
55 |
56 |
57 |
58 | [💝 Support this project and my other Open Source work](https://www.patreon.com/sanityinc)
59 |
60 | [💼 LinkedIn profile](https://uk.linkedin.com/in/stevepurcell)
61 |
62 | [✍ sanityinc.com](http://www.sanityinc.com/)
63 |
--------------------------------------------------------------------------------
/ibuffer-projectile.el:
--------------------------------------------------------------------------------
1 | ;;; ibuffer-projectile.el --- Group ibuffer's list by projectile root -*- lexical-binding: t -*-
2 | ;;
3 | ;; Copyright (C) 2011-2014 Steve Purcell
4 | ;;
5 | ;; Author: Steve Purcell
6 | ;; Keywords: convenience
7 | ;; Package-Requires: ((projectile "0.11.0") (emacs "25.1") (seq "2"))
8 | ;; URL: https://github.com/purcell/ibuffer-projectile
9 | ;; Package-Version: 0.4
10 | ;;
11 | ;; This program is free software; you can redistribute it and/or modify
12 | ;; it under the terms of the GNU General Public License as published by
13 | ;; the Free Software Foundation, either version 3 of the License, or
14 | ;; (at your option) any later version.
15 | ;;
16 | ;; This program is distributed in the hope that it will be useful,
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | ;; GNU General Public License for more details.
20 | ;;
21 | ;; You should have received a copy of the GNU General Public License
22 | ;; along with this program. If not, see .
23 | ;;
24 | ;;; Commentary:
25 | ;;
26 | ;; Adds functionality to ibuffer for grouping buffers by their projectile
27 | ;; root directory.
28 | ;;
29 | ;;; Use:
30 | ;;
31 | ;; To group buffers by projectile root dir:
32 | ;;
33 | ;; M-x ibuffer-projectile-set-filter-groups
34 | ;;
35 | ;; or, make this the default:
36 | ;;
37 | ;; (add-hook 'ibuffer-hook
38 | ;; (lambda ()
39 | ;; (ibuffer-projectile-set-filter-groups)
40 | ;; (unless (eq ibuffer-sorting-mode 'alphabetic)
41 | ;; (ibuffer-do-sort-by-alphabetic))))
42 | ;;
43 | ;; Alternatively, use `ibuffer-projectile-generate-filter-groups'
44 | ;; to programmatically obtain a list of filter groups that you can
45 | ;; combine with your own custom groups.
46 | ;;
47 | ;; To display filenames relative to the project root, use project-relative-file
48 | ;; in `ibuffer-formats', e.g.:
49 | ;;
50 | ;; (setq ibuffer-formats
51 | ;; '((mark modified read-only " "
52 | ;; (name 18 18 :left :elide)
53 | ;; " "
54 | ;; (size 9 -1 :right)
55 | ;; " "
56 | ;; (mode 16 16 :left :elide)
57 | ;; " "
58 | ;; project-relative-file)))
59 |
60 | ;;; Code:
61 |
62 | (require 'ibuffer)
63 | (require 'ibuf-ext)
64 | (require 'projectile)
65 | (require 'seq)
66 |
67 |
68 | (defgroup ibuffer-projectile nil
69 | "Group ibuffer entries according to their projectile root directory."
70 | :prefix "ibuffer-projectile-"
71 | :group 'convenience)
72 |
73 | (defcustom ibuffer-projectile-skip-if-remote t
74 | "If non-nil, don't query the status of remote files."
75 | :type 'boolean
76 | :group 'ibuffer-projectile)
77 |
78 | (defcustom ibuffer-projectile-include-function 'identity
79 | "A function which tells whether a given file should be grouped.
80 |
81 | The function is passed a filename, and should return non-nil if the file
82 | is to be grouped.
83 |
84 | This option can be used to exclude certain files from the grouping mechanism."
85 | :type 'function
86 | :group 'ibuffer-projectile)
87 |
88 | (defcustom ibuffer-projectile-prefix "Projectile:"
89 | "Prefix string for generated filter groups."
90 | :type 'string
91 | :group 'ibuffer-projectile)
92 |
93 | (defcustom ibuffer-projectile-group-name-function 'ibuffer-projectile-default-group-name
94 | "Function used to produce the name for a group.
95 | The function is passed two arguments: the projectile project
96 | name, and the root directory path."
97 | :type 'function
98 | :group 'ibuffer-projectile)
99 |
100 | (defun ibuffer-projectile-default-group-name (project-name root-dir)
101 | "Produce an ibuffer group name string for PROJECT-NAME and ROOT-DIR."
102 | (format "%s%s" ibuffer-projectile-prefix project-name))
103 |
104 | (defun ibuffer-projectile--include-file-p (file)
105 | "Return t iff FILE should be included in ibuffer-projectile's filtering."
106 | (and file
107 | (or (null ibuffer-projectile-skip-if-remote)
108 | (not (file-remote-p file)))
109 | (funcall ibuffer-projectile-include-function file)))
110 |
111 | (defun ibuffer-projectile-root (buf)
112 | "Return a cons cell (project-name . root-dir) for BUF.
113 | If the file is not in a project, then nil is returned instead."
114 | (with-current-buffer buf
115 | (let ((file-name (ibuffer-buffer-file-name))
116 | (root (ignore-errors (projectile-project-root))))
117 | (when (and file-name
118 | root
119 | (ibuffer-projectile--include-file-p file-name))
120 | (cons (projectile-project-name) root)))))
121 |
122 | (define-ibuffer-filter projectile-root
123 | "Toggle current view to buffers with projectile root dir QUALIFIER."
124 | (:description "projectile root dir"
125 | :reader (read-regexp "Filter by projectile root dir (regexp): "))
126 | (when-let ((it (ibuffer-projectile-root buf)))
127 | (if (stringp qualifier)
128 | (or (string-match-p qualifier (car it))
129 | (string-match-p qualifier (cdr-safe it)))
130 | (equal qualifier it))))
131 |
132 | ;;;###autoload (autoload 'ibuffer-make-column-project-name "ibuffer-projectile")
133 | (define-ibuffer-column project-name
134 | (:name "Project")
135 | (projectile-project-name))
136 |
137 | ;;;###autoload (autoload 'ibuffer-do-sort-by-project-name "ibuffer-projectile")
138 | (define-ibuffer-sorter project-name
139 | "Sort the buffers by their project name."
140 | (:description "project")
141 | (let ((project1 (with-current-buffer (car a)
142 | (projectile-project-name)))
143 | (project2 (with-current-buffer (car b)
144 | (projectile-project-name))))
145 | (if (and project1 project2)
146 | (string-lessp project1 project2)
147 | (not (null project1)))))
148 |
149 | ;;;###autoload (autoload 'ibuffer-make-column-project-relative-file "ibuffer-projectile")
150 | (define-ibuffer-column project-relative-file
151 | (:name "Filename")
152 | (when buffer-file-name
153 | (let ((root (cdr (ibuffer-projectile-root buffer))))
154 | (if root
155 | (file-relative-name buffer-file-name root)
156 | (abbreviate-file-name buffer-file-name)))))
157 |
158 | ;;;###autoload
159 | (defun ibuffer-projectile-generate-filter-groups ()
160 | "Create a set of ibuffer filter groups based on the projectile root dirs of buffers."
161 | (let ((roots (seq-uniq
162 | (delq nil (mapcar 'ibuffer-projectile-root (buffer-list))))))
163 | (mapcar (lambda (root)
164 | (cons (funcall ibuffer-projectile-group-name-function (car root) (cdr root))
165 | `((projectile-root . ,root))))
166 | roots)))
167 |
168 | ;;;###autoload
169 | (defun ibuffer-projectile-set-filter-groups ()
170 | "Set the current filter groups to filter by projectile root dir."
171 | (interactive)
172 | (setq ibuffer-filter-groups (ibuffer-projectile-generate-filter-groups))
173 | (message "ibuffer-projectile: groups set")
174 | (when-let ((ibuf (get-buffer "*Ibuffer*")))
175 | (with-current-buffer ibuf
176 | (pop-to-buffer ibuf)
177 | (ibuffer-update nil t))))
178 |
179 |
180 | (provide 'ibuffer-projectile)
181 | ;;; ibuffer-projectile.el ends here
182 |
--------------------------------------------------------------------------------