├── README.md └── ibuffer-projectile.el /README.md: -------------------------------------------------------------------------------- 1 | [![Melpa Status](http://melpa.org/packages/ibuffer-projectile-badge.svg)](http://melpa.org/#/ibuffer-projectile) 2 | [![Melpa Stable Status](http://stable.melpa.org/packages/ibuffer-projectile-badge.svg)](http://stable.melpa.org/#/ibuffer-projectile) 3 | Support me 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 | --------------------------------------------------------------------------------