@@ -254,10 +254,22 @@ the current version of the file)."
254254(defcustom diff-hl-update-async nil
255255 " When non-nil, `diff-hl-update' will run asynchronously.
256256
257- This can help prevent Emacs from freezing, especially by a slow version
258- control (VC) backend. It's disabled in remote buffers, though, since it
259- didn't work reliably in such during testing."
260- :type 'boolean )
257+ When the value is `thread' , it will call the diff process in a Lisp thread.
258+
259+ Any other non-nil value will make the the diff process called
260+ asynchronously in the main thread.
261+
262+ Note that the latter mechanism is only compatible with a recent Emacs
263+ 31+ (for built-in backends such as Git and Hg). Whereas using a thread can
264+ help with older Emacs as well, but might crash in some configurations.
265+
266+ This feature makes Emacs more responsive with slower version control (VC)
267+ backends and large projects. But it's disabled in remote buffers, since
268+ current testing shows it doesn't work reliably over Tramp."
269+ :type '(choice
270+ (const :tag " Disabled" nil )
271+ (const :tag " Simple async" t )
272+ (const :tag " Thread" thread)))
261273
262274; ; Threads are not reliable with remote files, yet.
263275(defcustom diff-hl-async-inhibit-functions (list #'diff-hl-with-editor-p
@@ -397,9 +409,9 @@ It can be a relative expression as well, such as \"HEAD^\" with Git, or
397409(declare-function vc-hg-command " vc-hg" )
398410(declare-function vc-bzr-command " vc-bzr" )
399411
400- (defun diff-hl-changes-buffer (file backend &optional new-rev )
412+ (defun diff-hl-changes-buffer (file backend &optional new-rev bufname )
401413 (diff-hl-with-diff-switches
402- (diff-hl-diff-against-reference file backend " *diff-hl* " new-rev)))
414+ (diff-hl-diff-against-reference file backend ( or bufname " *diff-hl* " ) new-rev)))
403415
404416(defun diff-hl-diff-against-reference (file backend buffer &optional new-rev )
405417 (cond
@@ -450,33 +462,32 @@ It can be a relative expression as well, such as \"HEAD^\" with Git, or
450462 ; ; This is fiddly, but we basically allow the thread to start, while
451463 ; ; prohibiting the async process call inside.
452464 ; ; That still makes it partially async.
453- (diff-hl-update-async (and diff-hl-update-async
454- (not ( eq window-system 'ns ) ))))
465+ (diff-hl-update-async (and ( not ( eq window-system 'ns ))
466+ (eq diff-hl-update-async t ))))
455467 (cond
456468 ((and
457469 (not diff-hl-highlight-reference-function)
458470 (diff-hl-modified-p state))
459- `((:working . ,(diff-hl-changes-from-buffer
460- (diff-hl-changes-buffer file backend)))))
471+ `((:working . ,(diff-hl-changes-buffer file backend))))
461472 ((or
462473 diff-hl-reference-revision
463474 (diff-hl-modified-p state))
464475 (let* ((ref-changes
465476 (and (or diff-hl-reference-revision
466477 hide-staged)
467- (diff-hl-changes-from-buffer
468- (diff-hl-changes-buffer file backend (if hide-staged
469- 'git-index
470- (diff-hl-head-revision backend))))))
478+ (diff-hl-changes-buffer file backend
479+ (if hide-staged
480+ 'git-index
481+ (diff-hl-head-revision backend))
482+ " *diff-hl-reference* " )))
471483 (diff-hl-reference-revision nil )
472- (work-changes (diff-hl-changes-from-buffer
473- (diff-hl-changes-buffer file backend))))
474- `((:reference . ,(diff-hl-adjust-changes ref-changes work-changes))
484+ (work-changes (diff-hl-changes-buffer file backend)))
485+ `((:reference . ,ref-changes )
475486 (:working . ,work-changes ))))
476487 ((eq state 'added )
477- `((:working . ((1 ,(line-number-at-pos (point-max )) 0 insert)))))
488+ `((:working . ,` ((1 ,(line-number-at-pos (point-max )) 0 insert)))))
478489 ((eq state 'removed )
479- `((:working . ((1 0 ,(line-number-at-pos (point-max )) delete))))))))))
490+ `((:working . ,` ((1 0 ,(line-number-at-pos (point-max )) delete))))))))))
480491
481492(defvar diff-hl-head-revision-alist '((Git . " HEAD" ) (Bzr . " last:1" ) (Hg . " ." )))
482493
@@ -550,7 +561,6 @@ contents as they are (or would be) after applying the changes in NEW."
550561 (accept-process-output proc 0.01 ))))
551562
552563(defun diff-hl-changes-from-buffer (buf )
553- (diff-hl-process-wait buf)
554564 (with-current-buffer buf
555565 (let (res)
556566 (goto-char (point-min ))
@@ -591,7 +601,8 @@ contents as they are (or would be) after applying the changes in NEW."
591601(defun diff-hl-update ()
592602 " Updates the diff-hl overlay."
593603 (setq diff-hl-timer nil )
594- (if (diff-hl--use-async-p)
604+ (if (and (diff-hl--use-async-p)
605+ (eq diff-hl-update-async 'thread ))
595606 ; ; TODO: debounce if a thread is already running.
596607 (let ((buf (current-buffer ))
597608 (temp-buffer
@@ -614,7 +625,7 @@ contents as they are (or would be) after applying the changes in NEW."
614625
615626(defun diff-hl--update-safe ()
616627 " Updates the diff-hl overlay. It handles and logs when an error is signaled."
617- (condition-case err
628+ (condition-case-unless-debug err
618629 (diff-hl--update)
619630 (error
620631 (message " An error occurred in diff-hl--update: %S " err)
@@ -667,19 +678,33 @@ Return a list of line overlays used."
667678 (nreverse ovls)))
668679
669680(defun diff-hl--update ()
670- (let* ((cc (diff-hl-changes))
671- (ref-changes (assoc-default :reference cc))
672- (changes (assoc-default :working cc))
673- reuse)
674- (diff-hl-remove-overlays)
675- (let ((diff-hl-highlight-function
676- diff-hl-highlight-reference-function)
677- (diff-hl-fringe-face-function
678- diff-hl-fringe-reference-face-function))
679- (setq reuse (diff-hl--update-overlays ref-changes nil )))
680- (diff-hl--update-overlays changes reuse)
681- (when (not (or changes ref-changes))
682- (diff-hl--autohide-margin))))
681+ (let* ((orig (current-buffer ))
682+ (cc (diff-hl-changes)))
683+ (diff-hl--resolve
684+ (assoc-default :working cc)
685+ (lambda (changes )
686+ (diff-hl--resolve
687+ (assoc-default :reference cc)
688+ (lambda (ref-changes )
689+ (let ((ref-changes (diff-hl-adjust-changes ref-changes changes))
690+ reuse)
691+ (with-current-buffer orig
692+ (diff-hl-remove-overlays)
693+ (let ((diff-hl-highlight-function
694+ diff-hl-highlight-reference-function)
695+ (diff-hl-fringe-face-function
696+ diff-hl-fringe-reference-face-function))
697+ (setq reuse (diff-hl--update-overlays ref-changes nil )))
698+ (diff-hl--update-overlays changes reuse)
699+ (when (not (or changes ref-changes))
700+ (diff-hl--autohide-margin))))))))))
701+
702+ (defun diff-hl--resolve (value-or-buffer cb )
703+ (if (listp value-or-buffer)
704+ (funcall cb value-or-buffer)
705+ (with-current-buffer value-or-buffer
706+ (vc-run-delayed
707+ (funcall cb (diff-hl-changes-from-buffer (current-buffer )))))))
683708
684709(defun diff-hl--autohide-margin ()
685710 (let ((width-var (intern (format " %s -margin-width" diff-hl-side))))
0 commit comments