Skip to content

Commit fefd2ed

Browse files
authored
Merge pull request #247 from EugeneNeuron/feature-project-local-reference
Support using reference revision as project-local
2 parents 74bfb70 + a31f1a5 commit fefd2ed

1 file changed

Lines changed: 165 additions & 26 deletions

File tree

diff-hl.el

Lines changed: 165 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -282,12 +282,22 @@ and passed the value `default-directory'.
282282
If any returns non-nil, `diff-hl-update' will run synchronously anyway."
283283
:type '(repeat :tag "Predicate" function))
284284

285+
(defvar diff-hl-reference-revision-projects-cache '()
286+
"Alist of cached directory roots for per-project reference revisions.
287+
Each element in this list has the form (DIR . REV).
288+
DIR is the expanded name of the directory.
289+
REV is the current reference revision.")
290+
285291
(defvar diff-hl-reference-revision nil
286292
"Revision to diff against. nil means the most recent one.
287293
288294
It can be a relative expression as well, such as \"HEAD^\" with Git, or
289295
\"-2\" with Mercurial.")
290296

297+
(put 'diff-hl-reference-revision 'safe-local-variable
298+
(lambda (value)
299+
(or (null value) (stringp value))))
300+
291301
(defun diff-hl-define-bitmaps ()
292302
(let* ((scale (if (and (boundp 'text-scale-mode-amount)
293303
(numberp text-scale-mode-amount))
@@ -1282,7 +1292,13 @@ The value of this variable is a mode line template as in
12821292
;; Magit versions 2.0-2.3 don't do the above and call this
12831293
;; instead, but only when they don't call `revert-buffer':
12841294
(add-hook 'magit-not-reverted-hook 'diff-hl-update nil t)
1285-
(add-hook 'text-scale-mode-hook 'diff-hl-maybe-redefine-bitmaps nil t))
1295+
(add-hook 'text-scale-mode-hook 'diff-hl-maybe-redefine-bitmaps nil t)
1296+
(when-let* ((rev (map-some
1297+
(lambda (root rev)
1298+
(when (string-prefix-p root default-directory)
1299+
rev))
1300+
diff-hl-reference-revision-projects-cache)))
1301+
(setq-local diff-hl-reference-revision rev)))
12861302
(remove-hook 'after-save-hook 'diff-hl-update t)
12871303
(remove-hook 'after-change-functions 'diff-hl-edit t)
12881304
(remove-hook 'find-file-hook 'diff-hl-update-once t)
@@ -1291,7 +1307,8 @@ The value of this variable is a mode line template as in
12911307
(remove-hook 'magit-not-reverted-hook 'diff-hl-update t)
12921308
(remove-hook 'text-scale-mode-hook 'diff-hl-maybe-redefine-bitmaps t)
12931309
(diff-hl-remove-overlays)
1294-
(diff-hl--autohide-margin)))
1310+
(diff-hl--autohide-margin)
1311+
(kill-local-variable 'diff-hl-reference-revision)))
12951312

12961313
(defun diff-hl-after-checkin ()
12971314
(let ((fileset (vc-deduce-fileset t)))
@@ -1556,6 +1573,11 @@ CONTEXT-LINES is the size of the unified diff context, defaults to 0."
15561573
"Set the reference revision globally to REV.
15571574
When called interactively, REV read with completion.
15581575
1576+
When called with a prefix argument, reset the global reference to the most
1577+
recent one instead. With two prefix arguments, do the same and discard
1578+
every per-project reference created by
1579+
`diff-hl-set-reference-rev-in-project`.
1580+
15591581
The default value chosen using one of methods below:
15601582
15611583
- In a log view buffer, it uses the revision of current entry.
@@ -1564,42 +1586,159 @@ view buffer.
15641586
- In a VC annotate buffer, it uses the revision of current line.
15651587
- In other situations, it uses the symbol at point.
15661588
1567-
Notice that this sets the reference revision globally, so in
1568-
files from other repositories, `diff-hl-mode' will not highlight
1569-
changes correctly, until you run `diff-hl-reset-reference-rev'.
1589+
Notice that this sets the reference revision globally, so in files from
1590+
other repositories, `diff-hl-mode' will not highlight changes correctly,
1591+
until you run `diff-hl-reset-reference-rev'. To set the reference on a
1592+
per-project basis, see `diff-hl-set-reference-rev-in-project`.
15701593
15711594
Also notice that this will disable `diff-hl-amend-mode' in
15721595
buffers that enables it, since `diff-hl-amend-mode' overrides its
15731596
effect."
15741597
(interactive
1575-
(let* ((def (or (and (equal major-mode 'vc-annotate-mode)
1576-
(car (vc-annotate-extract-revision-at-line)))
1577-
(log-view-current-tag)
1578-
(thing-at-point 'symbol t)))
1579-
(prompt (if def
1580-
(format "Reference revision (default %s): " def)
1581-
"Reference revision: ")))
1582-
(list (vc-read-revision prompt nil nil def))))
1583-
(if rev
1584-
(message "Set reference revision to %s" rev)
1598+
(if current-prefix-arg current-prefix-arg
1599+
(let* ((def (or (and (equal major-mode 'vc-annotate-mode)
1600+
(car (vc-annotate-extract-revision-at-line)))
1601+
(log-view-current-tag)
1602+
(thing-at-point 'symbol t)))
1603+
(prompt (if def
1604+
(format "Reference revision (default %s): " def)
1605+
"Reference revision: ")))
1606+
(list (vc-read-revision prompt nil nil def)))))
1607+
(unless rev
15851608
(user-error "No reference revision specified"))
1586-
(setq diff-hl-reference-revision rev)
1609+
(cond
1610+
((equal '(4) current-prefix-arg)
1611+
;; reset global value
1612+
(diff-hl-reset-reference-rev))
1613+
((equal '(16) current-prefix-arg)
1614+
;; reset global value and remove per-project value
1615+
(diff-hl-reset-reference-rev '(4)))
1616+
;; change only global value
1617+
(t (setq-default diff-hl-reference-revision rev)
1618+
(unless current-prefix-arg
1619+
(message "Set global reference revision to %s" rev))
1620+
(dolist (buf (buffer-list))
1621+
(with-current-buffer buf
1622+
(when diff-hl-mode
1623+
(when (bound-and-true-p diff-hl-amend-mode)
1624+
(diff-hl-amend-mode -1))
1625+
(when (not (local-variable-p 'diff-hl-reference-revision))
1626+
(diff-hl-update))))))))
1627+
1628+
;;;###autoload
1629+
(defun diff-hl-set-reference-rev-in-project (rev)
1630+
"Set the reference revision in the current project to REV.
1631+
When called interactively, REV read with completion.
1632+
1633+
When called with a prefix argument, reset to the global value instead.
1634+
1635+
The default value chosen using one of methods below:
1636+
1637+
- In a log view buffer, it uses the revision of current entry.
1638+
Call `vc-print-log' or `vc-print-root-log' first to open a log
1639+
view buffer.
1640+
- In a VC annotate buffer, it uses the revision of current line.
1641+
- In other situations, it uses the symbol at point.
1642+
1643+
Projects whose reference was set with this command are unaffected by
1644+
subsequent changes to the global reference (see
1645+
`diff-hl-set-reference-rev`).
1646+
1647+
Also notice that this will disable `diff-hl-amend-mode' in
1648+
buffers that enables it, since `diff-hl-amend-mode' overrides its
1649+
effect."
1650+
(interactive
1651+
(if current-prefix-arg current-prefix-arg
1652+
(let* ((def (or (and (equal major-mode 'vc-annotate-mode)
1653+
(car (vc-annotate-extract-revision-at-line)))
1654+
(log-view-current-tag)
1655+
(thing-at-point 'symbol t)))
1656+
(prompt (if def
1657+
(format "Reference revision (default %s): " def)
1658+
"Reference revision: ")))
1659+
(list (vc-read-revision prompt nil nil def)))))
1660+
(unless rev
1661+
(user-error "No reference revision specified"))
1662+
(let* ((proj (project-current))
1663+
(name (project-name proj)))
1664+
(cond
1665+
;; reset
1666+
(current-prefix-arg
1667+
(diff-hl-reset-reference-rev-in-project proj))
1668+
;; set
1669+
(t
1670+
(diff-hl-set-reference-rev-in-project-internal rev proj)
1671+
(message "Showing changes against %s (project %s)" rev name)))))
1672+
1673+
(defun diff-hl--project-root (proj)
1674+
;; Emacs 26 and 27 don't have `project-root'.
1675+
(expand-file-name
1676+
(or (and (fboundp 'project-root) (project-root proj))
1677+
(project-roots proj))))
1678+
1679+
(defun diff-hl-set-reference-rev-in-project-internal (rev proj)
1680+
(let* ((root (diff-hl--project-root proj)))
1681+
;; newly opened files will share this value
1682+
(setf (alist-get root diff-hl-reference-revision-projects-cache
1683+
nil nil #'string-equal)
1684+
rev)
1685+
;; update currently open files
1686+
(dolist (buf (project-buffers proj))
1687+
(with-current-buffer buf
1688+
(when diff-hl-mode
1689+
(when (bound-and-true-p diff-hl-amend-mode)
1690+
(diff-hl-amend-mode -1))
1691+
(setq-local diff-hl-reference-revision rev)
1692+
(diff-hl-update))))))
1693+
1694+
;;;###autoload
1695+
(defun diff-hl-reset-reference-rev (&optional arg)
1696+
"Reset the reference revision globally to the most recent one.
1697+
1698+
When called with a prefix argument, do the same and discard every
1699+
per-project reference created by `diff-hl-set-reference-rev-in-project'."
1700+
(interactive "P")
1701+
(setq-default diff-hl-reference-revision nil)
1702+
(when arg
1703+
;; reset all cache
1704+
(setq diff-hl-reference-revision-projects-cache nil))
15871705
(dolist (buf (buffer-list))
15881706
(with-current-buffer buf
15891707
(when diff-hl-mode
1708+
(when arg
1709+
;; reset value in buffers
1710+
(kill-local-variable 'diff-hl-reference-revision))
15901711
(when (bound-and-true-p diff-hl-amend-mode)
15911712
(diff-hl-amend-mode -1))
1592-
(diff-hl-update)))))
1593-
1594-
;;;###autoload
1595-
(defun diff-hl-reset-reference-rev ()
1596-
"Reset the reference revision globally to the most recent one."
1713+
;; Don't touch buffers with the local reference (set by
1714+
;; `diff-hl-set-reference-rev-in-project' ), when called without a
1715+
;; prefix.
1716+
(unless (local-variable-p 'diff-hl-reference-revision)
1717+
(diff-hl-update)))))
1718+
(message "Reference revision reset globally to the most recent revision"))
1719+
1720+
(defun diff-hl-reset-reference-rev-in-project (&optional proj)
1721+
"Reset the reference revision in the project PROJ to the
1722+
global value.
1723+
1724+
PROJ defaults to the current project."
15971725
(interactive)
1598-
(setq diff-hl-reference-revision nil)
1599-
(dolist (buf (buffer-list))
1600-
(with-current-buffer buf
1601-
(when diff-hl-mode
1602-
(diff-hl-update)))))
1726+
(when-let* ((proj (or proj (project-current))))
1727+
;; reset cache for the project
1728+
(setq diff-hl-reference-revision-projects-cache
1729+
(assoc-delete-all (diff-hl--project-root proj)
1730+
diff-hl-reference-revision-projects-cache
1731+
#'string-equal))
1732+
;; reset value in project buffers
1733+
(dolist (buf (project-buffers proj))
1734+
(with-current-buffer buf
1735+
(when diff-hl-mode
1736+
(when (bound-and-true-p diff-hl-amend-mode)
1737+
(diff-hl-amend-mode -1))
1738+
(kill-local-variable 'diff-hl-reference-revision)
1739+
(diff-hl-update))))
1740+
(message "Reference revision reset to the global value (project %s)"
1741+
(project-name proj))))
16031742

16041743
;;;###autoload
16051744
(define-globalized-minor-mode global-diff-hl-mode diff-hl-mode

0 commit comments

Comments
 (0)