]> code.delx.au - gnu-emacs-elpa/commitdiff
Merge commit '7fa7f099c7008d521dbbb57a1546f11535544536' from diff-hl
authorDmitry Gutov <dgutov@yandex.ru>
Wed, 9 Sep 2015 18:58:50 +0000 (21:58 +0300)
committerDmitry Gutov <dgutov@yandex.ru>
Wed, 9 Sep 2015 18:58:50 +0000 (21:58 +0300)
1  2 
packages/diff-hl/README.md
packages/diff-hl/diff-hl-dired.el
packages/diff-hl/diff-hl-flydiff.el
packages/diff-hl/diff-hl-margin.el
packages/diff-hl/diff-hl.el

index 325f53ea570c349cbb6f346eafedf7f4106ef82c,ab8c9d81e25031b5a87fcc172033b2651a8892ed..ab8c9d81e25031b5a87fcc172033b2651a8892ed
@@@ -15,6 -15,8 +15,8 @@@ The package also contains auxiliary mod
  * `diff-hl-margin-mode` changes the highlighting function to\r
    use the margin instead of the fringe.\r
  * `diff-hl-amend-mode` shifts the reference revision back by one.\r
+ * `diff-hl-flydiff-mode` implements highlighting changes on the fly.\r
+   It requires Emacs 24.4 or newer.\r
  \r
  Check out the Commentary section in each respective file for the usage\r
  instructions.\r
@@@ -46,10 -48,9 +48,9 @@@ Emacs 24+. On OS X, Emacs 24.3 or highe
  Notes\r
  =====\r
  \r
- * Since it uses the corresponding VC diff command, it's only accurate when the\r
-   buffer is in saved state. Highlighting changes "on the fly" might be better,\r
-   maybe we can do something similar to `highlight-markup-buffers` with a hidden\r
-   buffer containing the unmodified copy.\r
+ * By default `diff-hl-mode` uses the corresponding VC diff command, so\r
+   it's only accurate when the buffer is in saved state. Check out\r
+   `diff-hl-flydiff-mode`, it aims to handle unsaved buffers as well.\r
  \r
  * We conflict with other modes when they put indicators on the fringe,\r
    such as [Flycheck](https://github.com/flycheck/flycheck). This is\r
@@@ -63,6 -64,9 +64,9 @@@
  \r
  * Frame-local and buffer-local values of `line-spacing` are not supported.\r
  \r
+ * Fringe width up to 16 works best (because we can't define a bitmap\r
+   with width above that number).\r
\r
  * [emacs-git-gutter](https://github.com/syohex/emacs-git-gutter) shows\r
    indicators in the margin by default, allows you to customize how the\r
    indicators look more easily, and has a "stage hunk" command.\r
@@@ -87,4 -91,4 +91,4 @@@ Magi
  -----\r
  \r
  If you have a recent enough version installed, it defines\r
- `magit-revert-buffer-hook`, which we use.\r
+ `magit-revert-buffer-hook` (or `magit-not-reverted-hook`), which we use.\r
index 09cf851ccdcb73557649774cc1c72c7ccd1d8e0d,663d2938fdb1642178b8bd84b029a7a13c079172..663d2938fdb1642178b8bd84b029a7a13c079172
@@@ -1,6 -1,6 +1,6 @@@
  ;;; diff-hl-dired.el --- Highlight changed files in Dired -*- lexical-binding: t -*-
  
- ;; Copyright (C) 2012-2014  Free Software Foundation, Inc.
+ ;; Copyright (C) 2012-2015  Free Software Foundation, Inc.
  
  ;; This file is part of GNU Emacs.
  
                    (append dirs-alist files-alist))))))
           )))))
  
- (defun diff-hl-dired-status-files (backend dir files uf)
+ (defun diff-hl-dired-status-files (backend dir files update-function)
+    "Using version control BACKEND, return list of (FILE STATE EXTRA) entries
+ for DIR containing FILES. Call UPDATE-FUNCTION as entries are added."
    (if (version< "25" emacs-version)
-       (vc-call-backend backend 'dir-status-files dir files uf)
-     (vc-call-backend backend 'dir-status-files dir files nil uf)))
+       (vc-call-backend backend 'dir-status-files dir files update-function)
+     (vc-call-backend backend 'dir-status-files dir files nil update-function)))
  
  (when (version< emacs-version "24.4.51.5")
    ;; Work around http://debbugs.gnu.org/19386
  
  (defalias 'diff-hl-dired-clear 'diff-hl-remove-overlays)
  
+ ;;;###autoload
  (defun diff-hl-dired-mode-unless-remote ()
    (unless (file-remote-p default-directory)
      (diff-hl-dired-mode)))
index 0000000000000000000000000000000000000000,53d31f7496655102ea608164adf28f096470104a..53d31f7496655102ea608164adf28f096470104a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,182 +1,182 @@@
+ ;; Copyright (C) 2015 Free Software Foundation, Inc.
+ ;; Author:   Jonathan Hayase <PythonNut@gmail.com>
+ ;; URL:      https://github.com/dgutov/diff-hl
+ ;; This file is part of GNU Emacs.
+ ;; GNU Emacs 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.
+ ;; GNU Emacs 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 GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+ ;;; Commentary:
+ ;; This mode enables diffing on-the-fly (i.e. without saving the buffer first)
+ ;; Toggle in all buffers with M-x diff-hl-flydiff-mode
+ ;;; Code:
+ (require 'diff-hl)
+ (require 'diff)
+ (unless (require 'nadvice nil t)
+   (error "`diff-hl-flydiff-mode' requires Emacs 24.4 or newer"))
+ (defgroup diff-hl-flydiff nil
+   "Highlight changes on the fly"
+   :group 'diff-hl)
+ (defcustom diff-hl-flydiff-delay 0.3
+   "The idle delay in seconds before highlighting is updated."
+   :type 'number)
+ (defvar diff-hl-flydiff-modified-tick 0)
+ (defvar diff-hl-flydiff-timer)
+ (make-variable-buffer-local 'diff-hl-flydiff-modified-tick)
+ (defun diff-hl-flydiff/vc-git--symbolic-ref (file)
+   (or
+    (vc-file-getprop file 'vc-git-symbolic-ref)
+    (let* (process-file-side-effects
+           (str (vc-git--run-command-string nil "symbolic-ref" "HEAD")))
+      (vc-file-setprop file 'vc-git-symbolic-ref
+                       (if str
+                           (if (string-match "^\\(refs/heads/\\)?\\(.+\\)$" str)
+                               (match-string 2 str)
+                             str))))))
+ (defun diff-hl-flydiff/vc-git-working-revision (_file)
+   "Git-specific version of `vc-working-revision'."
+   (let (process-file-side-effects)
+     (vc-git--rev-parse "HEAD")))
+ (defun diff-hl-flydiff/vc-git-mode-line-string (file)
+   "Return a string for `vc-mode-line' to put in the mode line for FILE."
+   (let* ((rev (vc-working-revision file))
+          (disp-rev (or (diff-hl-flydiff/vc-git--symbolic-ref file)
+                        (substring rev 0 7)))
+          (def-ml (vc-default-mode-line-string 'Git file))
+          (help-echo (get-text-property 0 'help-echo def-ml))
+          (face   (get-text-property 0 'face def-ml)))
+     (propertize (replace-regexp-in-string (concat rev "\\'") disp-rev def-ml t t)
+                 'face face
+                 'help-echo (concat help-echo "\nCurrent revision: " rev))))
+ ;; Polyfill concrete revisions for vc-git-working-revision in Emacs 24.4, 24.5
+ (when (version<= emacs-version "25.0")
+   (with-eval-after-load 'vc-git
+     (advice-add 'vc-git-working-revision :override
+                 #'diff-hl-flydiff/vc-git-working-revision)
+     (advice-add 'vc-git-mode-line-string :override
+                 #'diff-hl-flydiff/vc-git-mode-line-string)))
+ (defun diff-hl-flydiff/working-revision (file)
+   "Like vc-working-revision, but always up-to-date"
+   (vc-file-setprop file 'vc-working-revision
+                    (vc-call-backend (vc-backend file) 'working-revision file)))
+ (defun diff-hl-flydiff-make-temp-file-name (file rev &optional manual)
+   "Return a backup file name for REV or the current version of FILE.
+ If MANUAL is non-nil it means that a name for backups created by
+ the user should be returned."
+   (let* ((auto-save-file-name-transforms
+           `((".*" ,temporary-file-directory t))))
+     (expand-file-name
+      (concat (make-auto-save-file-name)
+              ".~" (subst-char-in-string
+                    ?/ ?_ rev)
+              (unless manual ".") "~")
+      temporary-file-directory)))
+ (defun diff-hl-flydiff-create-revision (file revision)
+   "Read REVISION of FILE into a buffer and return the buffer."
+   (let ((automatic-backup (diff-hl-flydiff-make-temp-file-name file revision))
+         (filebuf (get-file-buffer file))
+         (filename (diff-hl-flydiff-make-temp-file-name file revision 'manual)))
+     (unless (file-exists-p filename)
+       (if (file-exists-p automatic-backup)
+           (rename-file automatic-backup filename nil)
+         (with-current-buffer filebuf
+           (let ((failed t)
+                 (coding-system-for-read 'no-conversion)
+                 (coding-system-for-write 'no-conversion))
+             (unwind-protect
+                 (with-temp-file filename
+                   (let ((outbuf (current-buffer)))
+                     ;; Change buffer to get local value of
+                     ;; vc-checkout-switches.
+                     (with-current-buffer filebuf
+                       (vc-call find-revision file revision outbuf))))
+               (setq failed nil)
+               (when (and failed (file-exists-p filename))
+                 (delete-file filename)))))))
+     filename))
+ (defun diff-hl-flydiff-buffer-with-head (file &optional backend)
+   "View the differences between BUFFER and its associated file.
+ This requires the external program `diff' to be in your `exec-path'."
+   (interactive)
+   (vc-ensure-vc-buffer)
+   (with-current-buffer (get-buffer (current-buffer))
+     (let* ((temporary-file-directory
+             (if (file-directory-p "/dev/shm/")
+                 "/dev/shm/"
+               temporary-file-directory))
+            (rev (diff-hl-flydiff-create-revision
+                  file
+                  (diff-hl-flydiff/working-revision file))))
+       (diff-no-select rev (current-buffer) "-U 0 --strip-trailing-cr" 'noasync
+                       (get-buffer-create " *diff-hl-diff*")))))
+ (defun diff-hl-flydiff/update (old-fun &optional auto)
+   (unless (and auto
+                (or
+                 (= diff-hl-flydiff-modified-tick (buffer-modified-tick))
+                 (file-remote-p default-directory)
+                 (not (buffer-modified-p))))
+     (funcall old-fun)))
+ (defun diff-hl-flydiff/modified-p (state)
+   (buffer-modified-p))
+ (defun diff-hl-flydiff/update-modified-tick (&rest args)
+   (setq diff-hl-flydiff-modified-tick (buffer-modified-tick)))
+ ;;;###autoload
+ (define-minor-mode diff-hl-flydiff-mode
+   "Highlight diffs on-the-fly"
+   :lighter ""
+   :global t
+   (if diff-hl-flydiff-mode
+       (progn
+         (advice-add 'diff-hl-update :around #'diff-hl-flydiff/update)
+         (advice-add 'diff-hl-overlay-modified :override #'ignore)
+         (advice-add 'diff-hl-modified-p :before-until
+                     #'diff-hl-flydiff/modified-p)
+         (advice-add 'diff-hl-changes-buffer :override
+                     #'diff-hl-flydiff-buffer-with-head)
+         (advice-add 'diff-hl-change :after
+                     #'diff-hl-flydiff/update-modified-tick)
+         (setq diff-hl-flydiff-timer
+               (run-with-idle-timer diff-hl-flydiff-delay t #'diff-hl-update t)))
+     (advice-remove 'diff-hl-update #'diff-hl-flydiff/update)
+     (advice-remove 'diff-hl-overlay-modified #'ignore)
+     (advice-remove 'diff-hl-modified-p #'diff-hl-flydiff/modified-p)
+     (advice-remove 'diff-hl-changes-buffer #'diff-hl-flydiff-buffer-with-head)
+     (advice-remove 'diff-hl-change #'diff-hl-flydiff/update-modified-tick)
+     (cancel-timer diff-hl-flydiff-timer)))
+ (provide 'diff-hl-flydiff)
index 26fa7ad781ae67a0db9993d29944bf9792be5b29,32aa1237e91026fc73c9e9f1a388ca0596d26501..32aa1237e91026fc73c9e9f1a388ca0596d26501
@@@ -1,6 -1,6 +1,6 @@@
  ;;; diff-hl-margin.el --- Highlight buffer changes on margins -*- lexical-binding: t -*-
  
- ;; Copyright (C) 2012-2014  Free Software Foundation, Inc.
+ ;; Copyright (C) 2012-2015  Free Software Foundation, Inc.
  
  ;; This file is part of GNU Emacs.
  
    "Highlight buffer changes on margin"
    :group 'diff-hl)
  
- (defcustom diff-hl-margin-side 'left
-   "Which margin to use for indicators."
-   :type '(choice (const left)
-                  (const right))
-   :set (lambda (var value)
-          (let ((on diff-hl-margin-mode))
-            (when on (diff-hl-margin-mode -1))
-            (set-default var value)
-            (when on (diff-hl-margin-mode 1)))))
  ;;;###autoload
  (define-minor-mode diff-hl-margin-mode
    "Toggle displaying `diff-hl-mode' highlights on the margin."
@@@ -82,7 -72,7 +72,7 @@@
    "Toggle displaying `diff-hl-mode' highlights on the margin locally.
  You probably shouldn't use this function directly."
    :lighter ""
-   (let ((width-var (intern (format "%s-margin-width" diff-hl-margin-side))))
+   (let ((width-var (intern (format "%s-margin-width" diff-hl-side))))
      (if diff-hl-margin-minor-mode
          (progn
            (set (make-local-variable 'diff-hl-margin-old-highlight-function)
@@@ -96,6 -86,8 +86,8 @@@
    (dolist (win (get-buffer-window-list))
      (set-window-buffer win (current-buffer))))
  
+ (define-obsolete-variable-alias 'diff-hl-margin-side 'diff-hl-side "1.7.1")
  (defun diff-hl-margin-minor-mode-off ()
    (diff-hl-margin-minor-mode -1))
  
                                         (intern (format "diff-hl-%s" type)))))))))
  
  (defun diff-hl-highlight-on-margin (ovl type _shape)
-   (let ((spec (cdr (assoc (cons type diff-hl-margin-side)
+   (let ((spec (cdr (assoc (cons type diff-hl-side)
                            diff-hl-margin-spec-cache))))
      (overlay-put ovl 'before-string spec)))
  
index 22a877a641353ac42af10828e1801ecd65e3bb93,8790352f10dad28b882affc7d6a474013aa5a3b6..8790352f10dad28b882affc7d6a474013aa5a3b6
- ;;; diff-hl.el --- Highlight uncommitted changes -*- lexical-binding: t -*-\r
\r
- ;; Copyright (C) 2012-2014  Free Software Foundation, Inc.\r
\r
- ;; Author:   Dmitry Gutov <dgutov@yandex.ru>\r
- ;; URL:      https://github.com/dgutov/diff-hl\r
- ;; Keywords: vc, diff\r
- ;; Version:  1.7.0\r
- ;; Package-Requires: ((cl-lib "0.2"))\r
\r
- ;; This file is part of GNU Emacs.\r
\r
- ;; GNU Emacs is free software: you can redistribute it and/or modify\r
- ;; it under the terms of the GNU General Public License as published by\r
- ;; the Free Software Foundation, either version 3 of the License, or\r
- ;; (at your option) any later version.\r
\r
- ;; GNU Emacs is distributed in the hope that it will be useful,\r
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- ;; GNU General Public License for more details.\r
\r
- ;; You should have received a copy of the GNU General Public License\r
- ;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.\r
\r
- ;;; Commentary:\r
\r
- ;; `diff-hl-mode' highlights uncommitted changes on the left side of\r
- ;; the window (using the fringe, by default), allows you to jump\r
- ;; between the hunks and revert them selectively.\r
\r
- ;; Provided commands:\r
- ;;\r
- ;; `diff-hl-diff-goto-hunk'  C-x v =\r
- ;; `diff-hl-revert-hunk'     C-x v n\r
- ;; `diff-hl-previous-hunk'   C-x v [\r
- ;; `diff-hl-next-hunk'       C-x v ]\r
- ;;\r
- ;; The mode takes advantage of `smartrep' if it is installed.\r
\r
- ;; Add either of the following to your init file.\r
- ;;\r
- ;; To use it in all buffers:\r
- ;;\r
- ;; (global-diff-hl-mode)\r
- ;;\r
- ;; Only in `prog-mode' buffers, with `vc-dir' integration:\r
- ;;\r
- ;; (add-hook 'prog-mode-hook 'turn-on-diff-hl-mode)\r
- ;; (add-hook 'vc-dir-mode-hook 'turn-on-diff-hl-mode)\r
\r
- ;;; Code:\r
\r
- (require 'fringe)\r
- (require 'diff-mode)\r
- (require 'vc)\r
- (require 'vc-dir)\r
- (eval-when-compile\r
-   (require 'cl-lib)\r
-   (require 'vc-git)\r
-   (require 'vc-hg)\r
-   (require 'face-remap))\r
\r
- (defgroup diff-hl nil\r
-   "VC diff highlighting on the side of a window"\r
-   :group 'vc)\r
\r
- (defface diff-hl-insert\r
-   '((default :inherit diff-added)\r
-     (((class color)) :foreground "green4"))\r
-   "Face used to highlight inserted lines."\r
-   :group 'diff-hl)\r
\r
- (defface diff-hl-delete\r
-   '((default :inherit diff-removed)\r
-     (((class color)) :foreground "red3"))\r
-   "Face used to highlight deleted lines."\r
-   :group 'diff-hl)\r
\r
- (defface diff-hl-change\r
-   '((default :foreground "blue3")\r
-     (((class color) (min-colors 88) (background light))\r
-      :background "#ddddff")\r
-     (((class color) (min-colors 88) (background dark))\r
-      :background "#333355"))\r
-   "Face used to highlight changed lines."\r
-   :group 'diff-hl)\r
\r
- (defcustom diff-hl-command-prefix (kbd "C-x v")\r
-   "The prefix for all `diff-hl' commands."\r
-   :group 'diff-hl\r
-   :type 'string)\r
\r
- (defcustom diff-hl-draw-borders t\r
-   "Non-nil to draw borders around fringe indicators."\r
-   :group 'diff-hl\r
-   :type 'boolean)\r
\r
- (defcustom diff-hl-highlight-function 'diff-hl-highlight-on-fringe\r
-   "Function to highlight the current line. Its arguments are\r
-   overlay, change type and position within a hunk."\r
-   :group 'diff-hl\r
-   :type 'function)\r
\r
- (defcustom diff-hl-fringe-bmp-function 'diff-hl-fringe-bmp-from-pos\r
-   "Function to choose the fringe bitmap for a given change type\r
-   and position within a hunk.  Should accept two arguments."\r
-   :group 'diff-hl\r
-   :type '(choice (const diff-hl-fringe-bmp-from-pos)\r
-                  (const diff-hl-fringe-bmp-from-type)\r
-                  function))\r
\r
- (defcustom diff-hl-fringe-face-function 'diff-hl-fringe-face-from-type\r
-   "Function to choose the fringe face for a given change type\r
-   and position within a hunk.  Should accept two arguments."\r
-   :group 'diff-hl\r
-   :type 'function)\r
\r
- (defvar diff-hl-reference-revision nil\r
-   "Revision to diff against.  nil means the most recent one.")\r
\r
- (defun diff-hl-define-bitmaps ()\r
-   (let* ((scale (if (and (boundp 'text-scale-mode-amount)\r
-                          (numberp text-scale-mode-amount))\r
-                     (expt text-scale-mode-step text-scale-mode-amount)\r
-                   1))\r
-          (spacing (or (and (display-graphic-p) (default-value 'line-spacing)) 0))\r
-          (h (+ (ceiling (* (frame-char-height) scale))\r
-                (if (floatp spacing)\r
-                    (truncate (* (frame-char-height) spacing))\r
-                  spacing)))\r
-          (w (frame-parameter nil 'left-fringe))\r
-          (middle (make-vector h (expt 2 (1- w))))\r
-          (ones (1- (expt 2 w)))\r
-          (top (copy-sequence middle))\r
-          (bottom (copy-sequence middle))\r
-          (single (copy-sequence middle)))\r
-     (aset top 0 ones)\r
-     (aset bottom (1- h) ones)\r
-     (aset single 0 ones)\r
-     (aset single (1- h) ones)\r
-     (define-fringe-bitmap 'diff-hl-bmp-top top h w 'top)\r
-     (define-fringe-bitmap 'diff-hl-bmp-middle middle h w 'center)\r
-     (define-fringe-bitmap 'diff-hl-bmp-bottom bottom h w 'bottom)\r
-     (define-fringe-bitmap 'diff-hl-bmp-single single h w 'top)\r
-     (define-fringe-bitmap 'diff-hl-bmp-i [3 3 0 3 3 3 3 3 3 3] nil 2 'center)\r
-     (let* ((w2 (* (/ w 2) 2))\r
-            ;; When fringes are disabled, it's easier to fix up the width,\r
-            ;; instead of doing nothing (#20).\r
-            (w2 (if (zerop w2) 2 w2))\r
-            (delete-row (- (expt 2 (1- w2)) 2))\r
-            (middle-pos (1- (/ w2 2)))\r
-            (middle-bit (expt 2 middle-pos))\r
-            (insert-bmp (make-vector w2 (* 3 middle-bit))))\r
-       (define-fringe-bitmap 'diff-hl-bmp-delete (make-vector 2 delete-row) w2 w2)\r
-       (aset insert-bmp 0 0)\r
-       (aset insert-bmp middle-pos delete-row)\r
-       (aset insert-bmp (1+ middle-pos) delete-row)\r
-       (aset insert-bmp (1- w2) 0)\r
-       (define-fringe-bitmap 'diff-hl-bmp-insert insert-bmp w2 w2)\r
-       )))\r
\r
- (defun diff-hl-maybe-define-bitmaps ()\r
-   (when (window-system) ;; No fringes in the console.\r
-     (unless (fringe-bitmap-p 'diff-hl-bmp-empty)\r
-       (diff-hl-define-bitmaps)\r
-       (define-fringe-bitmap 'diff-hl-bmp-empty [0] 1 1 'center))))\r
\r
- (defvar diff-hl-spec-cache (make-hash-table :test 'equal))\r
\r
- (defun diff-hl-fringe-spec (type pos)\r
-   (let* ((key (list type pos diff-hl-fringe-bmp-function))\r
-          (val (gethash key diff-hl-spec-cache)))\r
-     (unless val\r
-       (let* ((face-sym (funcall diff-hl-fringe-face-function type pos))\r
-              (bmp-sym (funcall diff-hl-fringe-bmp-function type pos)))\r
-         (setq val (propertize " " 'display `((left-fringe ,bmp-sym ,face-sym))))\r
-         (puthash key val diff-hl-spec-cache)))\r
-     val))\r
\r
- (defun diff-hl-fringe-face-from-type (type _pos)\r
-   (intern (format "diff-hl-%s" type)))\r
\r
- (defun diff-hl-fringe-bmp-from-pos (_type pos)\r
-   (intern (format "diff-hl-bmp-%s" pos)))\r
\r
- (defun diff-hl-fringe-bmp-from-type (type _pos)\r
-   (cl-case type\r
-     (unknown 'question-mark)\r
-     (change 'exclamation-mark)\r
-     (ignored 'diff-hl-bmp-i)\r
-     (t (intern (format "diff-hl-bmp-%s" type)))))\r
\r
- (defvar vc-svn-diff-switches)\r
\r
- (defmacro diff-hl-with-diff-switches (body)\r
-   `(let ((vc-git-diff-switches nil)\r
-          (vc-hg-diff-switches nil)\r
-          (vc-svn-diff-switches nil)\r
-          (vc-diff-switches '("-U0"))\r
-          (vc-disable-async-diff t))\r
-      ,body))\r
\r
- (defun diff-hl-changes ()\r
-   (let* ((file buffer-file-name)\r
-          (backend (vc-backend file)))\r
-     (when backend\r
-       (let ((state (vc-state file backend)))\r
-         (cond\r
-          ((or (eq state 'edited)\r
-               (and (eq state 'up-to-date)\r
-                    ;; VC state is stale in after-revert-hook.\r
-                    (or revert-buffer-in-progress-p\r
-                        ;; Diffing against an older revision.\r
-                        diff-hl-reference-revision)))\r
-           (let* ((buf-name " *diff-hl* ")\r
-                  diff-auto-refine-mode\r
-                  res)\r
-             (diff-hl-with-diff-switches\r
-              (vc-call-backend backend 'diff (list file)\r
-                               diff-hl-reference-revision nil\r
-                               buf-name))\r
-             (with-current-buffer buf-name\r
-               (goto-char (point-min))\r
-               (unless (eobp)\r
-                 (ignore-errors\r
-                   (diff-beginning-of-hunk t))\r
-                 (while (looking-at diff-hunk-header-re-unified)\r
-                   (let ((line (string-to-number (match-string 3)))\r
-                         (len (let ((m (match-string 4)))\r
-                                (if m (string-to-number m) 1)))\r
-                         (beg (point)))\r
-                     (diff-end-of-hunk)\r
-                     (let* ((inserts (diff-count-matches "^\\+" beg (point)))\r
-                            (deletes (diff-count-matches "^-" beg (point)))\r
-                            (type (cond ((zerop deletes) 'insert)\r
-                                        ((zerop inserts) 'delete)\r
-                                        (t 'change))))\r
-                       (when (eq type 'delete)\r
-                         (setq len 1)\r
-                         (cl-incf line))\r
-                       (push (list line len type) res))))))\r
-             (nreverse res)))\r
-          ((eq state 'added)\r
-           `((1 ,(line-number-at-pos (point-max)) insert)))\r
-          ((eq state 'removed)\r
-           `((1 ,(line-number-at-pos (point-max)) delete))))))))\r
\r
- (defun diff-hl-update ()\r
-   (let ((changes (diff-hl-changes))\r
-         (current-line 1))\r
-     (diff-hl-remove-overlays)\r
-     (save-excursion\r
-       (goto-char (point-min))\r
-       (dolist (c changes)\r
-         (cl-destructuring-bind (line len type) c\r
-           (forward-line (- line current-line))\r
-           (setq current-line line)\r
-           (let ((hunk-beg (point)))\r
-             (while (cl-plusp len)\r
-               (diff-hl-add-highlighting\r
-                type\r
-                (cond\r
-                 ((not diff-hl-draw-borders) 'empty)\r
-                 ((and (= len 1) (= line current-line)) 'single)\r
-                 ((= len 1) 'bottom)\r
-                 ((= line current-line) 'top)\r
-                 (t 'middle)))\r
-               (forward-line 1)\r
-               (cl-incf current-line)\r
-               (cl-decf len))\r
-             (let ((h (make-overlay hunk-beg (point)))\r
-                   (hook '(diff-hl-overlay-modified)))\r
-               (overlay-put h 'diff-hl t)\r
-               (overlay-put h 'diff-hl-hunk t)\r
-               (overlay-put h 'modification-hooks hook)\r
-               (overlay-put h 'insert-in-front-hooks hook)\r
-               (overlay-put h 'insert-behind-hooks hook))))))))\r
\r
- (defun diff-hl-add-highlighting (type shape)\r
-   (let ((o (make-overlay (point) (point))))\r
-     (overlay-put o 'diff-hl t)\r
-     (funcall diff-hl-highlight-function o type shape)\r
-     o))\r
\r
- (defun diff-hl-highlight-on-fringe (ovl type shape)\r
-   (overlay-put ovl 'before-string (diff-hl-fringe-spec type shape)))\r
\r
- (defun diff-hl-remove-overlays ()\r
-   (dolist (o (overlays-in (point-min) (point-max)))\r
-     (when (overlay-get o 'diff-hl) (delete-overlay o))))\r
\r
- (defun diff-hl-overlay-modified (ov after-p _beg _end &optional _length)\r
-   "Delete the hunk overlay and all our line overlays inside it."\r
-   (unless after-p\r
-     (when (overlay-buffer ov)\r
-       (save-restriction\r
-         (narrow-to-region (overlay-start ov) (overlay-end ov))\r
-         (diff-hl-remove-overlays))\r
-       (delete-overlay ov))))\r
\r
- (defvar diff-hl-timer nil)\r
\r
- (defun diff-hl-edit (_beg _end _len)\r
-   "DTRT when we've `undo'-ne the buffer into unmodified state."\r
-   (when undo-in-progress\r
-     (when diff-hl-timer\r
-       (cancel-timer diff-hl-timer))\r
-     (setq diff-hl-timer\r
-           (run-with-idle-timer 0.01 nil #'diff-hl-after-undo (current-buffer)))))\r
\r
- (defun diff-hl-after-undo (buffer)\r
-   (with-current-buffer buffer\r
-     (unless (buffer-modified-p)\r
-       (diff-hl-update))))\r
\r
- (defun diff-hl-diff-goto-hunk ()\r
-   "Run VC diff command and go to the line corresponding to the current."\r
-   (interactive)\r
-   (vc-buffer-sync)\r
-   (let* ((line (line-number-at-pos))\r
-          (buffer (current-buffer)))\r
-     (vc-diff-internal t (vc-deduce-fileset) diff-hl-reference-revision nil t)\r
-     (vc-exec-after `(if (< (line-number-at-pos (point-max)) 3)\r
-                         (with-current-buffer ,buffer (diff-hl-remove-overlays))\r
-                       (diff-hl-diff-skip-to ,line)\r
-                       (setq vc-sentinel-movepoint (point))))))\r
\r
- (defun diff-hl-diff-skip-to (line)\r
-   "In `diff-mode', skip to the hunk and line corresponding to LINE\r
- in the source file, or the last line of the hunk above it."\r
-   (diff-hunk-next)\r
-   (let (found)\r
-     (while (and (looking-at diff-hunk-header-re-unified) (not found))\r
-       (let ((hunk-line (string-to-number (match-string 3)))\r
-             (len (let ((m (match-string 4)))\r
-                    (if m (string-to-number m) 1))))\r
-         (if (> line (+ hunk-line len))\r
-             (diff-hunk-next)\r
-           (setq found t)\r
-           (if (< line hunk-line)\r
-               ;; Retreat to the previous hunk.\r
-               (forward-line -1)\r
-             (let ((to-go (1+ (- line hunk-line))))\r
-               (while (cl-plusp to-go)\r
-                 (forward-line 1)\r
-                 (unless (looking-at "^-")\r
-                   (cl-decf to-go))))))))))\r
\r
- (defun diff-hl-revert-hunk ()\r
-   "Revert the diff hunk with changes at or above the point."\r
-   (interactive)\r
-   (vc-buffer-sync)\r
-   (let ((diff-buffer (generate-new-buffer-name "*diff-hl*"))\r
-         (buffer (current-buffer))\r
-         (line (save-excursion\r
-                 (unless (diff-hl-hunk-overlay-at (point))\r
-                   (diff-hl-previous-hunk))\r
-                 (line-number-at-pos)))\r
-         (fileset (vc-deduce-fileset)))\r
-     (unwind-protect\r
-         (progn\r
-           (vc-diff-internal nil fileset diff-hl-reference-revision nil\r
-                             nil diff-buffer)\r
-           (vc-exec-after\r
-            `(let (beg-line end-line)\r
-               (when (eobp)\r
-                 (with-current-buffer ,buffer (diff-hl-remove-overlays))\r
-                 (error "Buffer is up-to-date"))\r
-               (diff-hl-diff-skip-to ,line)\r
-               (save-excursion\r
-                 (while (looking-at "[-+]") (forward-line 1))\r
-                 (setq end-line (line-number-at-pos (point)))\r
-                 (unless (eobp) (diff-split-hunk)))\r
-               (unless (looking-at "[-+]") (forward-line -1))\r
-               (while (looking-at "[-+]") (forward-line -1))\r
-               (setq beg-line (line-number-at-pos (point)))\r
-               (unless (looking-at "@")\r
-                 (forward-line 1)\r
-                 (diff-split-hunk))\r
-               (let ((wbh (window-body-height)))\r
-                 (if (>= wbh (- end-line beg-line))\r
-                     (recenter (/ (+ wbh (- beg-line end-line) 2) 2))\r
-                   (recenter 1)))\r
-               (unless (yes-or-no-p (format "Revert current hunk in %s?"\r
-                                            ,(cl-caadr fileset)))\r
-                 (error "Revert canceled"))\r
-               (let ((diff-advance-after-apply-hunk nil))\r
-                 (diff-apply-hunk t))\r
-               (with-current-buffer ,buffer\r
-                 (save-buffer))\r
-               (message "Hunk reverted"))))\r
-       (quit-windows-on diff-buffer))))\r
\r
- (defun diff-hl-hunk-overlay-at (pos)\r
-   (cl-loop for o in (overlays-in pos (1+ pos))\r
-            when (overlay-get o 'diff-hl-hunk)\r
-            return o))\r
\r
- (defun diff-hl-next-hunk (&optional backward)\r
-   "Go to the beginning of the next hunk in the current buffer."\r
-   (interactive)\r
-   (let ((pos (save-excursion\r
-                (catch 'found\r
-                  (while (not (if backward (bobp) (eobp)))\r
-                    (goto-char (if backward\r
-                                   (previous-overlay-change (point))\r
-                                 (next-overlay-change (point))))\r
-                    (let ((o (diff-hl-hunk-overlay-at (point))))\r
-                      (when (and o (= (overlay-start o) (point)))\r
-                        (throw 'found (overlay-start o)))))))))\r
-     (if pos\r
-         (goto-char pos)\r
-       (error "No further hunks found"))))\r
\r
- (defun diff-hl-previous-hunk ()\r
-   "Go to the beginning of the previous hunk in the current buffer."\r
-   (interactive)\r
-   (diff-hl-next-hunk t))\r
\r
- (define-prefix-command 'diff-hl-command-map)\r
\r
- (let ((map diff-hl-command-map))\r
-   (define-key map "n" 'diff-hl-revert-hunk)\r
-   (define-key map "[" 'diff-hl-previous-hunk)\r
-   (define-key map "]" 'diff-hl-next-hunk)\r
-   map)\r
\r
- ;;;###autoload\r
- (define-minor-mode diff-hl-mode\r
-   "Toggle VC diff highlighting."\r
-   :lighter "" :keymap `(([remap vc-diff] . diff-hl-diff-goto-hunk)\r
-                         (,diff-hl-command-prefix . diff-hl-command-map))\r
-   (if diff-hl-mode\r
-       (progn\r
-         (diff-hl-maybe-define-bitmaps)\r
-         (add-hook 'after-save-hook 'diff-hl-update nil t)\r
-         (add-hook 'after-change-functions 'diff-hl-edit nil t)\r
-         (add-hook (if vc-mode\r
-                       ;; Defer until the end of this hook, so that its\r
-                       ;; elements can modify the update behavior.\r
-                       'diff-hl-mode-on-hook\r
-                     ;; If we're only opening the file now,\r
-                     ;; `vc-find-file-hook' likely hasn't run yet, so\r
-                     ;; let's wait until the state information is\r
-                     ;; saved, in order not to fetch it twice.\r
-                     'find-file-hook)\r
-                   'diff-hl-update t t)\r
-         (add-hook 'vc-checkin-hook 'diff-hl-update nil t)\r
-         (add-hook 'after-revert-hook 'diff-hl-update nil t)\r
-         ;; Magit does call `auto-revert-handler', but it usually\r
-         ;; doesn't do much, because `buffer-stale--default-function'\r
-         ;; doesn't care about changed VC state.\r
-         ;; https://github.com/magit/magit/issues/603\r
-         (add-hook 'magit-revert-buffer-hook 'diff-hl-update nil t)\r
-         (add-hook 'auto-revert-mode-hook 'diff-hl-update nil t)\r
-         (add-hook 'text-scale-mode-hook 'diff-hl-define-bitmaps nil t))\r
-     (remove-hook 'after-save-hook 'diff-hl-update t)\r
-     (remove-hook 'after-change-functions 'diff-hl-edit t)\r
-     (remove-hook 'find-file-hook 'diff-hl-update t)\r
-     (remove-hook 'vc-checkin-hook 'diff-hl-update t)\r
-     (remove-hook 'after-revert-hook 'diff-hl-update t)\r
-     (remove-hook 'magit-revert-buffer-hook 'diff-hl-update t)\r
-     (remove-hook 'auto-revert-mode-hook 'diff-hl-update t)\r
-     (remove-hook 'text-scale-mode-hook 'diff-hl-define-bitmaps t)\r
-     (diff-hl-remove-overlays)))\r
\r
- (when (require 'smartrep nil t)\r
-   (let (smart-keys)\r
-     (cl-labels ((scan (map)\r
-                       (map-keymap\r
-                        (lambda (event binding)\r
-                          (if (consp binding)\r
-                              (scan binding)\r
-                            (when (characterp event)\r
-                              (push (cons (string event) binding) smart-keys))))\r
-                        map)))\r
-       (scan diff-hl-command-map)\r
-       (smartrep-define-key diff-hl-mode-map diff-hl-command-prefix smart-keys))))\r
\r
- (defun diff-hl-dir-update ()\r
-   (dolist (pair (if (vc-dir-marked-files)\r
-                     (vc-dir-marked-only-files-and-states)\r
-                   (vc-dir-child-files-and-states)))\r
-     (when (eq 'up-to-date (cdr pair))\r
-       (let ((buffer (find-buffer-visiting (car pair))))\r
-         (when buffer\r
-           (with-current-buffer buffer\r
-             (diff-hl-remove-overlays)))))))\r
\r
- (define-minor-mode diff-hl-dir-mode\r
-   "Toggle `diff-hl-mode' integration in a `vc-dir-mode' buffer."\r
-   :lighter ""\r
-   (if diff-hl-dir-mode\r
-       (add-hook 'vc-checkin-hook 'diff-hl-dir-update t t)\r
-     (remove-hook 'vc-checkin-hook 'diff-hl-dir-update t)))\r
\r
- ;;;###autoload\r
- (defun turn-on-diff-hl-mode ()\r
-   "Turn on `diff-hl-mode' or `diff-hl-dir-mode' in a buffer if appropriate."\r
-   (cond\r
-    (buffer-file-name\r
-     (diff-hl-mode 1))\r
-    ((eq major-mode 'vc-dir-mode)\r
-     (diff-hl-dir-mode 1))))\r
\r
- ;;;###autoload\r
- (define-globalized-minor-mode global-diff-hl-mode diff-hl-mode\r
-   turn-on-diff-hl-mode :after-hook (diff-hl-global-mode-change))\r
\r
- (defun diff-hl-global-mode-change ()\r
-   (unless global-diff-hl-mode\r
-     (dolist (buf (buffer-list))\r
-       (with-current-buffer buf\r
-         (when diff-hl-dir-mode\r
-           (diff-hl-dir-mode -1))))))\r
\r
- (provide 'diff-hl)\r
\r
- ;;; diff-hl.el ends here\r
+ ;;; diff-hl.el --- Highlight uncommitted changes -*- lexical-binding: t -*-
+ ;; Copyright (C) 2012-2015  Free Software Foundation, Inc.
+ ;; Author:   Dmitry Gutov <dgutov@yandex.ru>
+ ;; URL:      https://github.com/dgutov/diff-hl
+ ;; Keywords: vc, diff
+ ;; Version:  1.8.0
+ ;; Package-Requires: ((cl-lib "0.2"))
+ ;; This file is part of GNU Emacs.
+ ;; GNU Emacs 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.
+ ;; GNU Emacs 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 GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+ ;;; Commentary:
+ ;; `diff-hl-mode' highlights uncommitted changes on the side of the
+ ;; window (using the fringe, by default), allows you to jump between
+ ;; the hunks and revert them selectively.
+ ;; Provided commands:
+ ;;
+ ;; `diff-hl-diff-goto-hunk'  C-x v =
+ ;; `diff-hl-revert-hunk'     C-x v n
+ ;; `diff-hl-previous-hunk'   C-x v [
+ ;; `diff-hl-next-hunk'       C-x v ]
+ ;;
+ ;; The mode takes advantage of `smartrep' if it is installed.
+ ;; Add either of the following to your init file.
+ ;;
+ ;; To use it in all buffers:
+ ;;
+ ;; (global-diff-hl-mode)
+ ;;
+ ;; Only in `prog-mode' buffers, with `vc-dir' integration:
+ ;;
+ ;; (add-hook 'prog-mode-hook 'turn-on-diff-hl-mode)
+ ;; (add-hook 'vc-dir-mode-hook 'turn-on-diff-hl-mode)
+ ;;; Code:
+ (require 'fringe)
+ (require 'diff-mode)
+ (require 'vc)
+ (require 'vc-dir)
+ (eval-when-compile
+   (require 'cl-lib)
+   (require 'vc-git)
+   (require 'vc-hg)
+   (require 'face-remap)
+   (declare-function smartrep-define-key 'smartrep))
+ (defgroup diff-hl nil
+   "VC diff highlighting on the side of a window"
+   :group 'vc)
+ (defface diff-hl-insert
+   '((default :inherit diff-added)
+     (((class color)) :foreground "green4"))
+   "Face used to highlight inserted lines."
+   :group 'diff-hl)
+ (defface diff-hl-delete
+   '((default :inherit diff-removed)
+     (((class color)) :foreground "red3"))
+   "Face used to highlight deleted lines."
+   :group 'diff-hl)
+ (defface diff-hl-change
+   '((default :foreground "blue3")
+     (((class color) (min-colors 88) (background light))
+      :background "#ddddff")
+     (((class color) (min-colors 88) (background dark))
+      :background "#333355"))
+   "Face used to highlight changed lines."
+   :group 'diff-hl)
+ (defcustom diff-hl-command-prefix (kbd "C-x v")
+   "The prefix for all `diff-hl' commands."
+   :group 'diff-hl
+   :type 'string)
+ (defcustom diff-hl-draw-borders t
+   "Non-nil to draw borders around fringe indicators."
+   :group 'diff-hl
+   :type 'boolean)
+ (defcustom diff-hl-highlight-function 'diff-hl-highlight-on-fringe
+   "Function to highlight the current line. Its arguments are
+   overlay, change type and position within a hunk."
+   :group 'diff-hl
+   :type 'function)
+ (defcustom diff-hl-fringe-bmp-function 'diff-hl-fringe-bmp-from-pos
+   "Function to choose the fringe bitmap for a given change type
+   and position within a hunk.  Should accept two arguments."
+   :group 'diff-hl
+   :type '(choice (const diff-hl-fringe-bmp-from-pos)
+                  (const diff-hl-fringe-bmp-from-type)
+                  function))
+ (defcustom diff-hl-fringe-face-function 'diff-hl-fringe-face-from-type
+   "Function to choose the fringe face for a given change type
+   and position within a hunk.  Should accept two arguments."
+   :group 'diff-hl
+   :type 'function)
+ (defcustom diff-hl-side 'left
+   "Which side to use for indicators."
+   :type '(choice (const left)
+                  (const right))
+   :set (lambda (var value)
+          (let ((on (bound-and-true-p global-diff-hl-mode)))
+            (when on (global-diff-hl-mode -1))
+            (set-default var value)
+            (when on (global-diff-hl-mode 1)))))
+ (defvar diff-hl-reference-revision nil
+   "Revision to diff against.  nil means the most recent one.")
+ (defun diff-hl-define-bitmaps ()
+   (let* ((scale (if (and (boundp 'text-scale-mode-amount)
+                          (numberp text-scale-mode-amount))
+                     (expt text-scale-mode-step text-scale-mode-amount)
+                   1))
+          (spacing (or (and (display-graphic-p) (default-value 'line-spacing)) 0))
+          (h (+ (ceiling (* (frame-char-height) scale))
+                (if (floatp spacing)
+                    (truncate (* (frame-char-height) spacing))
+                  spacing)))
+          (w (min (frame-parameter nil (intern (format "%s-fringe" diff-hl-side)))
+                  16))
+          (middle (make-vector h (expt 2 (1- w))))
+          (ones (1- (expt 2 w)))
+          (top (copy-sequence middle))
+          (bottom (copy-sequence middle))
+          (single (copy-sequence middle)))
+     (aset top 0 ones)
+     (aset bottom (1- h) ones)
+     (aset single 0 ones)
+     (aset single (1- h) ones)
+     (define-fringe-bitmap 'diff-hl-bmp-top top h w 'top)
+     (define-fringe-bitmap 'diff-hl-bmp-middle middle h w 'center)
+     (define-fringe-bitmap 'diff-hl-bmp-bottom bottom h w 'bottom)
+     (define-fringe-bitmap 'diff-hl-bmp-single single h w 'top)
+     (define-fringe-bitmap 'diff-hl-bmp-i [3 3 0 3 3 3 3 3 3 3] nil 2 'center)
+     (let* ((w2 (* (/ w 2) 2))
+            ;; When fringes are disabled, it's easier to fix up the width,
+            ;; instead of doing nothing (#20).
+            (w2 (if (zerop w2) 2 w2))
+            (delete-row (- (expt 2 (1- w2)) 2))
+            (middle-pos (1- (/ w2 2)))
+            (middle-bit (expt 2 middle-pos))
+            (insert-bmp (make-vector w2 (* 3 middle-bit))))
+       (define-fringe-bitmap 'diff-hl-bmp-delete (make-vector 2 delete-row) w2 w2)
+       (aset insert-bmp 0 0)
+       (aset insert-bmp middle-pos delete-row)
+       (aset insert-bmp (1+ middle-pos) delete-row)
+       (aset insert-bmp (1- w2) 0)
+       (define-fringe-bitmap 'diff-hl-bmp-insert insert-bmp w2 w2)
+       )))
+ (defun diff-hl-maybe-define-bitmaps ()
+   (when (window-system) ;; No fringes in the console.
+     (unless (fringe-bitmap-p 'diff-hl-bmp-empty)
+       (diff-hl-define-bitmaps)
+       (define-fringe-bitmap 'diff-hl-bmp-empty [0] 1 1 'center))))
+ (defvar diff-hl-spec-cache (make-hash-table :test 'equal))
+ (defun diff-hl-fringe-spec (type pos side)
+   (let* ((key (list type pos side
+                     diff-hl-fringe-face-function
+                     diff-hl-fringe-bmp-function))
+          (val (gethash key diff-hl-spec-cache)))
+     (unless val
+       (let* ((face-sym (funcall diff-hl-fringe-face-function type pos))
+              (bmp-sym (funcall diff-hl-fringe-bmp-function type pos)))
+         (setq val (propertize " " 'display `((,(intern (format "%s-fringe" side))
+                                               ,bmp-sym ,face-sym))))
+         (puthash key val diff-hl-spec-cache)))
+     val))
+ (defun diff-hl-fringe-face-from-type (type _pos)
+   (intern (format "diff-hl-%s" type)))
+ (defun diff-hl-fringe-bmp-from-pos (_type pos)
+   (intern (format "diff-hl-bmp-%s" pos)))
+ (defun diff-hl-fringe-bmp-from-type (type _pos)
+   (cl-case type
+     (unknown 'question-mark)
+     (change 'exclamation-mark)
+     (ignored 'diff-hl-bmp-i)
+     (t (intern (format "diff-hl-bmp-%s" type)))))
+ (defvar vc-svn-diff-switches)
+ (defmacro diff-hl-with-diff-switches (body)
+   `(let ((vc-git-diff-switches nil)
+          (vc-hg-diff-switches nil)
+          (vc-svn-diff-switches nil)
+          (vc-diff-switches '("-U0"))
+          ,@(when (boundp 'vc-disable-async-diff)
+              '((vc-disable-async-diff t))))
+      ,body))
+ (defun diff-hl-modified-p (state)
+   (or (eq state 'edited)
+       (and (eq state 'up-to-date)
+            ;; VC state is stale in after-revert-hook.
+            (or revert-buffer-in-progress-p
+                ;; Diffing against an older revision.
+                diff-hl-reference-revision))))
+ (defun diff-hl-changes-buffer (file backend)
+   (let ((buf-name " *diff-hl* "))
+     (diff-hl-with-diff-switches
+      (vc-call-backend backend 'diff (list file)
+                       diff-hl-reference-revision nil
+                       buf-name))
+     buf-name))
+ (defun diff-hl-changes ()
+   (let* ((file buffer-file-name)
+          (backend (vc-backend file)))
+     (when backend
+       (let ((state (vc-state file backend)))
+         (cond
+          ((diff-hl-modified-p state)
+           (let* (diff-auto-refine-mode res)
+             (with-current-buffer (diff-hl-changes-buffer file backend)
+               (goto-char (point-min))
+               (unless (eobp)
+                 (ignore-errors
+                   (diff-beginning-of-hunk t))
+                 (while (looking-at diff-hunk-header-re-unified)
+                   (let ((line (string-to-number (match-string 3)))
+                         (len (let ((m (match-string 4)))
+                                (if m (string-to-number m) 1)))
+                         (beg (point)))
+                     (diff-end-of-hunk)
+                     (let* ((inserts (diff-count-matches "^\\+" beg (point)))
+                            (deletes (diff-count-matches "^-" beg (point)))
+                            (type (cond ((zerop deletes) 'insert)
+                                        ((zerop inserts) 'delete)
+                                        (t 'change))))
+                       (when (eq type 'delete)
+                         (setq len 1)
+                         (cl-incf line))
+                       (push (list line len type) res))))))
+             (nreverse res)))
+          ((eq state 'added)
+           `((1 ,(line-number-at-pos (point-max)) insert)))
+          ((eq state 'removed)
+           `((1 ,(line-number-at-pos (point-max)) delete))))))))
+ (defun diff-hl-update ()
+   (let ((changes (diff-hl-changes))
+         (current-line 1))
+     (diff-hl-remove-overlays)
+     (save-excursion
+       (save-restriction
+         (widen)
+         (goto-char (point-min))
+         (dolist (c changes)
+           (cl-destructuring-bind (line len type) c
+             (forward-line (- line current-line))
+             (setq current-line line)
+             (let ((hunk-beg (point)))
+               (while (cl-plusp len)
+                 (diff-hl-add-highlighting
+                  type
+                  (cond
+                   ((not diff-hl-draw-borders) 'empty)
+                   ((and (= len 1) (= line current-line)) 'single)
+                   ((= len 1) 'bottom)
+                   ((= line current-line) 'top)
+                   (t 'middle)))
+                 (forward-line 1)
+                 (cl-incf current-line)
+                 (cl-decf len))
+               (let ((h (make-overlay hunk-beg (point)))
+                     (hook '(diff-hl-overlay-modified)))
+                 (overlay-put h 'diff-hl t)
+                 (overlay-put h 'diff-hl-hunk t)
+                 (overlay-put h 'modification-hooks hook)
+                 (overlay-put h 'insert-in-front-hooks hook)
+                 (overlay-put h 'insert-behind-hooks hook)))))))))
+ (defun diff-hl-add-highlighting (type shape)
+   (let ((o (make-overlay (point) (point))))
+     (overlay-put o 'diff-hl t)
+     (funcall diff-hl-highlight-function o type shape)
+     o))
+ (defun diff-hl-highlight-on-fringe (ovl type shape)
+   (overlay-put ovl 'before-string (diff-hl-fringe-spec type shape
+                                                        diff-hl-side)))
+ (defun diff-hl-remove-overlays (&optional beg end)
+   (save-restriction
+     (widen)
+     (dolist (o (overlays-in (or beg (point-min)) (or end (point-max))))
+       (when (overlay-get o 'diff-hl) (delete-overlay o)))))
+ (defun diff-hl-overlay-modified (ov after-p _beg _end &optional _length)
+   "Delete the hunk overlay and all our line overlays inside it."
+   (unless after-p
+     (when (overlay-buffer ov)
+       (diff-hl-remove-overlays (overlay-start ov) (overlay-end ov))
+       (delete-overlay ov))))
+ (defvar diff-hl-timer nil)
+ (defun diff-hl-edit (_beg _end _len)
+   "DTRT when we've `undo'-ne the buffer into unmodified state."
+   (when undo-in-progress
+     (when diff-hl-timer
+       (cancel-timer diff-hl-timer))
+     (setq diff-hl-timer
+           (run-with-idle-timer 0.01 nil #'diff-hl-after-undo (current-buffer)))))
+ (defun diff-hl-after-undo (buffer)
+   (with-current-buffer buffer
+     (unless (buffer-modified-p)
+       (diff-hl-update))))
+ (defun diff-hl-diff-goto-hunk ()
+   "Run VC diff command and go to the line corresponding to the current."
+   (interactive)
+   (vc-buffer-sync)
+   (let* ((line (line-number-at-pos))
+          (buffer (current-buffer)))
+     (vc-diff-internal t (vc-deduce-fileset) diff-hl-reference-revision nil t)
+     (vc-exec-after `(if (< (line-number-at-pos (point-max)) 3)
+                         (with-current-buffer ,buffer (diff-hl-remove-overlays))
+                       (diff-hl-diff-skip-to ,line)
+                       (setq vc-sentinel-movepoint (point))))))
+ (defun diff-hl-diff-skip-to (line)
+   "In `diff-mode', skip to the hunk and line corresponding to LINE
+ in the source file, or the last line of the hunk above it."
+   (diff-hunk-next)
+   (let (found)
+     (while (and (looking-at diff-hunk-header-re-unified) (not found))
+       (let ((hunk-line (string-to-number (match-string 3)))
+             (len (let ((m (match-string 4)))
+                    (if m (string-to-number m) 1))))
+         (if (> line (+ hunk-line len))
+             (diff-hunk-next)
+           (setq found t)
+           (if (< line hunk-line)
+               ;; Retreat to the previous hunk.
+               (forward-line -1)
+             (let ((to-go (1+ (- line hunk-line))))
+               (while (cl-plusp to-go)
+                 (forward-line 1)
+                 (unless (looking-at "^-")
+                   (cl-decf to-go))))))))))
+ (defun diff-hl-revert-hunk ()
+   "Revert the diff hunk with changes at or above the point."
+   (interactive)
+   (vc-buffer-sync)
+   (let ((diff-buffer (generate-new-buffer-name "*diff-hl*"))
+         (buffer (current-buffer))
+         (line (save-excursion
+                 (unless (diff-hl-hunk-overlay-at (point))
+                   (diff-hl-previous-hunk))
+                 (line-number-at-pos)))
+         (fileset (vc-deduce-fileset)))
+     (unwind-protect
+         (progn
+           (vc-diff-internal nil fileset diff-hl-reference-revision nil
+                             nil diff-buffer)
+           (vc-exec-after
+            `(let (beg-line end-line)
+               (when (eobp)
+                 (with-current-buffer ,buffer (diff-hl-remove-overlays))
+                 (error "Buffer is up-to-date"))
+               (diff-hl-diff-skip-to ,line)
+               (save-excursion
+                 (while (looking-at "[-+]") (forward-line 1))
+                 (setq end-line (line-number-at-pos (point)))
+                 (unless (eobp) (diff-split-hunk)))
+               (unless (looking-at "[-+]") (forward-line -1))
+               (while (looking-at "[-+]") (forward-line -1))
+               (setq beg-line (line-number-at-pos (point)))
+               (unless (looking-at "@")
+                 (forward-line 1)
+                 (diff-split-hunk))
+               (let ((wbh (window-body-height)))
+                 (if (>= wbh (- end-line beg-line))
+                     (recenter (/ (+ wbh (- beg-line end-line) 2) 2))
+                   (recenter 1)))
+               (unless (yes-or-no-p (format "Revert current hunk in %s?"
+                                            ,(cl-caadr fileset)))
+                 (error "Revert canceled"))
+               (let ((diff-advance-after-apply-hunk nil))
+                 (diff-apply-hunk t))
+               (with-current-buffer ,buffer
+                 (save-buffer))
+               (message "Hunk reverted"))))
+       (quit-windows-on diff-buffer))))
+ (defun diff-hl-hunk-overlay-at (pos)
+   (cl-loop for o in (overlays-in pos (1+ pos))
+            when (overlay-get o 'diff-hl-hunk)
+            return o))
+ (defun diff-hl-next-hunk (&optional backward)
+   "Go to the beginning of the next hunk in the current buffer."
+   (interactive)
+   (let ((pos (save-excursion
+                (catch 'found
+                  (while (not (if backward (bobp) (eobp)))
+                    (goto-char (if backward
+                                   (previous-overlay-change (point))
+                                 (next-overlay-change (point))))
+                    (let ((o (diff-hl-hunk-overlay-at (point))))
+                      (when (and o (= (overlay-start o) (point)))
+                        (throw 'found (overlay-start o)))))))))
+     (if pos
+         (goto-char pos)
+       (error "No further hunks found"))))
+ (defun diff-hl-previous-hunk ()
+   "Go to the beginning of the previous hunk in the current buffer."
+   (interactive)
+   (diff-hl-next-hunk t))
+ (defvar diff-hl-command-map
+   (let ((map (make-sparse-keymap)))
+     (define-key map "n" 'diff-hl-revert-hunk)
+     (define-key map "[" 'diff-hl-previous-hunk)
+     (define-key map "]" 'diff-hl-next-hunk)
+     map))
+ (fset 'diff-hl-command-map diff-hl-command-map)
+ ;;;###autoload
+ (define-minor-mode diff-hl-mode
+   "Toggle VC diff highlighting."
+   :lighter "" :keymap `(([remap vc-diff] . diff-hl-diff-goto-hunk)
+                         (,diff-hl-command-prefix . diff-hl-command-map))
+   (if diff-hl-mode
+       (progn
+         (diff-hl-maybe-define-bitmaps)
+         (add-hook 'after-save-hook 'diff-hl-update nil t)
+         (add-hook 'after-change-functions 'diff-hl-edit nil t)
+         (add-hook (if vc-mode
+                       ;; Defer until the end of this hook, so that its
+                       ;; elements can modify the update behavior.
+                       'diff-hl-mode-on-hook
+                     ;; If we're only opening the file now,
+                     ;; `vc-find-file-hook' likely hasn't run yet, so
+                     ;; let's wait until the state information is
+                     ;; saved, in order not to fetch it twice.
+                     'find-file-hook)
+                   'diff-hl-update t t)
+         (add-hook 'vc-checkin-hook 'diff-hl-update nil t)
+         (add-hook 'after-revert-hook 'diff-hl-update nil t)
+         ;; Magit does call `auto-revert-handler', but it usually
+         ;; doesn't do much, because `buffer-stale--default-function'
+         ;; doesn't care about changed VC state.
+         ;; https://github.com/magit/magit/issues/603
+         (add-hook 'magit-revert-buffer-hook 'diff-hl-update nil t)
+         ;; Magit 2+ doesn't do the above and calls this instead,
+         ;; but only when it doesn't call `revert-buffer':
+         (add-hook 'magit-not-reverted-hook 'diff-hl-update nil t)
+         (add-hook 'auto-revert-mode-hook 'diff-hl-update nil t)
+         (add-hook 'text-scale-mode-hook 'diff-hl-define-bitmaps nil t))
+     (remove-hook 'after-save-hook 'diff-hl-update t)
+     (remove-hook 'after-change-functions 'diff-hl-edit t)
+     (remove-hook 'find-file-hook 'diff-hl-update t)
+     (remove-hook 'vc-checkin-hook 'diff-hl-update t)
+     (remove-hook 'after-revert-hook 'diff-hl-update t)
+     (remove-hook 'magit-revert-buffer-hook 'diff-hl-update t)
+     (remove-hook 'magit-not-reverted-hook 'diff-hl-update t)
+     (remove-hook 'auto-revert-mode-hook 'diff-hl-update t)
+     (remove-hook 'text-scale-mode-hook 'diff-hl-define-bitmaps t)
+     (diff-hl-remove-overlays)))
+ (when (require 'smartrep nil t)
+   (let (smart-keys)
+     (cl-labels ((scan (map)
+                       (map-keymap
+                        (lambda (event binding)
+                          (if (consp binding)
+                              (scan binding)
+                            (when (characterp event)
+                              (push (cons (string event) binding) smart-keys))))
+                        map)))
+       (scan diff-hl-command-map)
+       (smartrep-define-key diff-hl-mode-map diff-hl-command-prefix smart-keys))))
+ (defun diff-hl-dir-update ()
+   (dolist (pair (if (vc-dir-marked-files)
+                     (vc-dir-marked-only-files-and-states)
+                   (vc-dir-child-files-and-states)))
+     (when (eq 'up-to-date (cdr pair))
+       (let ((buffer (find-buffer-visiting (car pair))))
+         (when buffer
+           (with-current-buffer buffer
+             (diff-hl-remove-overlays)))))))
+ (define-minor-mode diff-hl-dir-mode
+   "Toggle `diff-hl-mode' integration in a `vc-dir-mode' buffer."
+   :lighter ""
+   (if diff-hl-dir-mode
+       (add-hook 'vc-checkin-hook 'diff-hl-dir-update t t)
+     (remove-hook 'vc-checkin-hook 'diff-hl-dir-update t)))
+ ;;;###autoload
+ (defun turn-on-diff-hl-mode ()
+   "Turn on `diff-hl-mode' or `diff-hl-dir-mode' in a buffer if appropriate."
+   (cond
+    (buffer-file-name
+     (diff-hl-mode 1))
+    ((eq major-mode 'vc-dir-mode)
+     (diff-hl-dir-mode 1))))
+ ;;;###autoload
+ (define-globalized-minor-mode global-diff-hl-mode diff-hl-mode
+   turn-on-diff-hl-mode :after-hook (diff-hl-global-mode-change))
+ (defun diff-hl-global-mode-change ()
+   (unless global-diff-hl-mode
+     (dolist (buf (buffer-list))
+       (with-current-buffer buf
+         (when diff-hl-dir-mode
+           (diff-hl-dir-mode -1))))))
+ (provide 'diff-hl)
+ ;;; diff-hl.el ends here