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