[
  {
    "path": ".github/FUNDING.yml",
    "content": "patreon: sanityinc\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n- package-ecosystem: github-actions\n  directory: \"/\"\n  schedule:\n    interval: daily\n  open-pull-requests-limit: 10\n  commit-message:\n    prefix: \"chore\"\n    include: \"scope\"\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: CI\n\non:\n  pull_request:\n  push:\n    paths-ignore:\n    - '**.md'\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: purcell/setup-emacs@master\n      with:\n        version: 29.4\n    - uses: actions/checkout@v6\n    - name: Run tests\n      run: make package-lint\n\n  build:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        emacs_version:\n          - 25.1\n          - 25.3\n          - 26.1\n          - 26.3\n          - 27.1\n          - 27.2\n          - 28.1\n          - 28.2\n          - 29.4\n          - snapshot\n    steps:\n    - uses: purcell/setup-emacs@master\n      with:\n        version: ${{ matrix.emacs_version }}\n\n    - uses: actions/checkout@v6\n    - name: Compile\n      run: make compile\n"
  },
  {
    "path": ".gitignore",
    "content": "/ibuffer-vc.elc\n"
  },
  {
    "path": "Makefile",
    "content": "EMACS ?= emacs\n\n# A space-separated list of required package names\nDEPS =\n\nINIT_PACKAGES=\"(progn \\\n  (require 'package) \\\n  (push '(\\\"melpa\\\" . \\\"https://melpa.org/packages/\\\") package-archives) \\\n  (package-initialize) \\\n  (dolist (pkg '(PACKAGES)) \\\n    (unless (package-installed-p pkg) \\\n      (unless (assoc pkg package-archive-contents) \\\n        (package-refresh-contents)) \\\n      (package-install pkg))) \\\n  )\"\n\nall: compile package-lint test clean-elc\n\npackage-lint:\n\t${EMACS} -Q --eval $(subst PACKAGES,package-lint,${INIT_PACKAGES}) -batch -f package-lint-batch-and-exit ibuffer-vc.el\n\ncompile: clean-elc\n\t${EMACS} -Q --eval $(subst PACKAGES,${DEPS},${INIT_PACKAGES}) -L . -batch -f batch-byte-compile *.el\n\nclean-elc:\n\trm -f f.elc\n\n.PHONY:\tall compile clean-elc package-lint\n"
  },
  {
    "path": "README.md",
    "content": "[![Melpa Status](http://melpa.org/packages/ibuffer-vc-badge.svg)](http://melpa.org/#/ibuffer-vc)\n[![Melpa Stable Status](http://stable.melpa.org/packages/ibuffer-vc-badge.svg)](http://stable.melpa.org/#/ibuffer-vc)\n[![Build Status](https://github.com/purcell/ibuffer-vc/actions/workflows/test.yml/badge.svg)](https://github.com/purcell/ibuffer-vc/actions/workflows/test.yml)\n<a href=\"https://www.patreon.com/sanityinc\"><img alt=\"Support me\" src=\"https://img.shields.io/badge/Support%20Me-%F0%9F%92%97-ff69b4.svg\"></a>\n\n# ibuffer-vc: Group buffers in ibuffer list by VC project #\n\nEmacs' `ibuffer-mode` is a wonderful replacement for the built-in\n`list-buffer` command, and allows buffers to be grouped\nprogramatically, e.g. by major mode.\n\nIf, like me, you mostly work on version-controlled files, you might\nlike to see your buffers grouped by the associated version control\nproject.\n\nThat's where `ibuffer-vc` comes in: it lets you:\n\n* Group your buffers by their parent vc root directory\n* See the VC status of the associated files\n* Sort buffers by their VC status\n* Display buffer filenames that are relative to their VC root\n\n## Screenshot ##\n\n![ibuffer-vc screenshot](http://i.imgur.com/RUYRJ.png)\n\n## How to install ##\n\nSee `ibuffer-vc.el`, or (preferred) install from [MELPA][MELPA].\n\n\n[MELPA]: http://melpa.org \"MELPA\"\n\n\n<hr>\n\n\n[💝 Support this project and my other Open Source work](https://www.patreon.com/sanityinc)\n\n[💼 LinkedIn profile](https://uk.linkedin.com/in/stevepurcell)\n\n[✍ sanityinc.com](http://www.sanityinc.com/)\n"
  },
  {
    "path": "ibuffer-vc.el",
    "content": ";;; ibuffer-vc.el --- Group ibuffer's list by VC project, or show VC status  -*- lexical-binding: t -*-\n;;\n;; Copyright (C) 2011-2014 Steve Purcell\n;;\n;; Author: Steve Purcell <steve@sanityinc.com>\n;; Keywords: convenience\n;; Package-Requires: ((emacs \"25.1\") (seq \"2\"))\n;; URL: https://github.com/purcell/ibuffer-vc\n;; Version: 0.12\n;;\n;; This program is free software; you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n;;\n;; This program is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n;;\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n;;\n;;; Commentary:\n;;\n;; Adds functionality to ibuffer for grouping buffers by their parent\n;; vc root directory, and for displaying and/or sorting by the vc\n;; status of listed files.\n;;\n;;; Use:\n;;\n;; To group buffers by vc parent dir:\n;;\n;;   M-x ibuffer-vc-set-filter-groups-by-vc-root\n;;\n;; or, make this the default:\n;;\n;;   (add-hook 'ibuffer-hook\n;;     (lambda ()\n;;       (ibuffer-vc-set-filter-groups-by-vc-root)\n;;       (unless (eq ibuffer-sorting-mode 'alphabetic)\n;;         (ibuffer-do-sort-by-alphabetic))))\n;;\n;; Alternatively, use `ibuffer-vc-generate-filter-groups-by-vc-root'\n;; to programmatically obtain a list of filter groups that you can\n;; combine with your own custom groups.\n;;\n;; To include vc status info in the ibuffer list, add either\n;; vc-status-mini or vc-status to `ibuffer-formats':\n;;\n;; (setq ibuffer-formats\n;;       '((mark modified read-only vc-status-mini \" \"\n;;               (name 18 18 :left :elide)\n;;               \" \"\n;;               (size 9 -1 :right)\n;;               \" \"\n;;               (mode 16 16 :left :elide)\n;;               \" \"\n;;               (vc-status 16 16 :left)\n;;               \" \"\n;;               vc-relative-file)))\n;;\n;; To sort by vc status, use `ibuffer-do-sort-by-vc-status', which can\n;; also be selected by repeatedly executing\n;; `ibuffer-toggle-sorting-mode' (bound to \",\" by default).\n;;\n;;; Code:\n\n;; requires\n\n(require 'ibuffer)\n(require 'ibuf-ext)\n(require 'vc-hooks)\n(require 'seq)\n\n\n(defgroup ibuffer-vc nil\n  \"Group ibuffer entries according to their version control status.\"\n  :prefix \"ibuffer-vc-\"\n  :group 'convenience)\n\n(defcustom ibuffer-vc-skip-if-remote t\n  \"If non-nil, don't query the VC status of remote files.\"\n  :type 'boolean\n  :group 'ibuffer-vc)\n\n(defcustom ibuffer-vc-include-function 'identity\n  \"A function which tells whether a given file should be grouped.\n\nThe function is passed a filename, and should return non-nil if the file\nis to be grouped.\n\nThis option can be used to exclude certain files from the grouping mechanism.\"\n  :type 'function\n  :group 'ibuffer-vc)\n\n(defcustom ibuffer-vc-buffer-file-name-function 'ibuffer-vc-buffer-file-truename\n  \"Function which tells the file name associated with a buffer.\n\nThe function is passed a buffer, and should return the file name\nto associate with that buffer.\n\nThis option can be configured along with\n`ibuffer-vc-include-function' to include or exclude certain\nbuffers from the grouping mechanism.\"\n  :type 'function\n  :group 'ibuffer-vc)\n\n;;; Group and filter ibuffer entries by parent vc directory\n\n(defun ibuffer-vc-buffer-file-truename (buf)\n  \"Return the truename of the file associated with BUF.\n\nThe file associated with BUF is the one that BUF is visiting,\nwhose file name is stored in the buffer-local variable\n`buffer-file-name'.\n\nIf that is nil, but BUF sets the variable\n`list-buffers-directory' (for example, Dired and Magit buffers),\nthen consider that directory to be the file associated with the\nbuffer.\n\nIf neither of those is set for BUF, return nil.\"\n  (with-current-buffer buf\n    (when-let ((file-name (or buffer-file-name\n                              list-buffers-directory)))\n      (file-truename file-name))))\n\n(defun ibuffer-vc--include-file-p (file)\n  \"Return t iff FILE should be included in ibuffer-vc's filtering.\"\n  (and file\n       (or (null ibuffer-vc-skip-if-remote)\n           (not (file-remote-p file)))\n       (funcall ibuffer-vc-include-function file)))\n\n(defun ibuffer-vc--deduce-backend (file)\n  \"Return the vc backend for FILE, or nil if not under VC supervision.\"\n  (if (fboundp 'vc-responsible-backend)\n      (ignore-errors (vc-responsible-backend file))\n    (or (vc-backend file)\n        (seq-filter (lambda (b) (vc-call-backend b 'responsible-p file)) vc-handled-backends))))\n\n(defun ibuffer-vc-root (buf)\n  \"Return a cons cell (backend-name . root-dir) for BUF.\nIf the file is not under version control, nil is returned instead.\"\n  (let ((file-name (funcall ibuffer-vc-buffer-file-name-function buf)))\n    (when (ibuffer-vc--include-file-p file-name)\n      (when-let ((backend (ibuffer-vc--deduce-backend file-name)))\n        (let* ((root-fn-name (intern (format \"vc-%s-root\" (downcase (symbol-name backend)))))\n               (root-dir\n                (cond\n                 ((fboundp root-fn-name) (funcall root-fn-name file-name)) ; git, svn, hg, bzr (at least)\n                 ((and (fboundp 'vc-darcs-find-root)                       ; vc-darcs is an external package\n                       (memq backend '(darcs DARCS)))\n                  (vc-darcs-find-root file-name))\n                 ((memq backend '(cvs CVS)) (vc-find-root file-name \"CVS\"))\n                 ((memq backend '(rcs RCS)) (or (vc-find-root file-name \"RCS\")\n                                                (concat file-name \",v\")))\n                 ((memq backend '(sccs SCCS)) (or (vc-find-root file-name \"SCCS\")\n                                                  (concat \"s.\" file-name)))\n                 ((memq backend '(src SRC)) (or (vc-find-root file-name \".src\")\n                                                (concat file-name \",v\")))\n                 (t (error \"ibuffer-vc: don't know how to find root for vc backend '%s' - please submit a bug report or patch\" backend)))))\n          (cons backend root-dir))))))\n\n(defun ibuffer-vc-read-filter ()\n  \"Read a cons cell of (backend-name . root-dir).\"\n  (cons (car (read-from-string\n              (completing-read \"VC backend: \" vc-handled-backends nil t)))\n        (read-directory-name \"Root directory: \" nil nil t)))\n\n(define-ibuffer-filter vc-root\n    \"Toggle current view to buffers with vc root dir QUALIFIER.\"\n  (:description \"vc root dir\"\n                :reader (ibuffer-vc-read-filter))\n  (when-let ((it (ibuffer-vc-root buf)))\n    (equal qualifier it)))\n\n;;;###autoload\n(defun ibuffer-vc-generate-filter-groups-by-vc-root ()\n  \"Create a set of ibuffer filter groups based on the vc root dirs of buffers.\"\n  (let ((roots (seq-uniq\n                (delq nil (mapcar 'ibuffer-vc-root (buffer-list))))))\n    (mapcar (lambda (vc-root)\n              (cons (format \"%s: %s\" (car vc-root) (cdr vc-root))\n                    `((vc-root . ,vc-root))))\n            roots)))\n\n;;;###autoload\n(defun ibuffer-vc-set-filter-groups-by-vc-root ()\n  \"Set the current filter groups to filter by vc root dir.\"\n  (interactive)\n  (setq ibuffer-filter-groups (ibuffer-vc-generate-filter-groups-by-vc-root))\n  (message \"ibuffer-vc: groups set\")\n  (when-let ((ibuf (get-buffer \"*Ibuffer*\")))\n    (with-current-buffer ibuf\n      (pop-to-buffer ibuf)\n      (ibuffer-update nil t))))\n\n\n;;; Display vc status info in the ibuffer list\n\n(defun ibuffer-vc--state (file)\n  \"Return the `vc-state' for FILE, or nil if unregistered.\"\n  (ignore-errors (vc-state file)))\n\n(defun ibuffer-vc--status-string ()\n  \"Return a short string to represent the current buffer's status.\"\n  (when (and buffer-file-name (ibuffer-vc--include-file-p buffer-file-name))\n    (let ((state (ibuffer-vc--state buffer-file-name)))\n      (if state\n          (symbol-name state)\n        \"-\"))))\n\n;;;###autoload (autoload 'ibuffer-make-column-vc-status \"ibuffer-vc\")\n(define-ibuffer-column vc-status\n  (:name \"VC status\")\n  (ibuffer-vc--status-string))\n\n;;;###autoload (autoload 'ibuffer-make-column-vc-relative-file \"ibuffer-vc\")\n(define-ibuffer-column vc-relative-file\n  (:name \"Filename\")\n  (if buffer-file-name\n      (let ((root (cdr (ibuffer-vc-root buffer))))\n        (if root\n            (file-relative-name buffer-file-name root)\n          (abbreviate-file-name buffer-file-name)))\n    \"\"))\n\n;;;###autoload (autoload 'ibuffer-make-column-vc-status-mini \"ibuffer-vc\")\n(define-ibuffer-column vc-status-mini\n  (:name \"V\")\n  (if (and buffer-file-name (ibuffer-vc--include-file-p buffer-file-name))\n      (let ((state (ibuffer-vc--state buffer-file-name)))\n        (cond\n         ((eq 'added state) \"A\")\n         ((eq 'removed state) \"D\")\n         ((eq 'up-to-date state) \"=\")\n         ((eq 'edited state) \"*\")\n         ((eq 'needs-update state) \"O\")\n         ((memq state '(conflict needs-merge unlocked-changes)) \"!\")\n         ((eq 'ignored state) \"I\")\n         ((memq state '(() unregistered missing)) \"?\")))\n    \" \"))\n\n;;;###autoload (autoload 'ibuffer-do-sort-by-vc-status \"ibuffer-vc\")\n(define-ibuffer-sorter vc-status\n  \"Sort the buffers by their vc status.\"\n  (:description \"vc status\")\n  (let ((file1 (with-current-buffer (car a)\n                 buffer-file-name))\n        (file2 (with-current-buffer (car b)\n                 buffer-file-name)))\n    (if (and file1 file2)\n        (string-lessp (with-current-buffer (car a)\n                        (ibuffer-vc--status-string))\n                      (with-current-buffer (car b)\n                        (ibuffer-vc--status-string)))\n      (not (null file1)))))\n\n\n(provide 'ibuffer-vc)\n;;; ibuffer-vc.el ends here\n"
  }
]