Skip to content

Commit 0e7c689

Browse files
authored
Merge pull request #246 from dgutov/async-no-thread
Introduce a no-thread async implementation
2 parents a44db7a + e3157b5 commit 0e7c689

2 files changed

Lines changed: 72 additions & 49 deletions

File tree

diff-hl.el

Lines changed: 60 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -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))))

test/diff-hl-test.el

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,11 @@
140140
(diff-hl-mode 1)
141141
(diff-hl-update)
142142

143-
;; wait for all thread to complete.
144-
(dolist (thread (all-threads))
145-
(unless (eq thread main-thread)
146-
(thread-join thread)))
143+
(while (or (process-live-p
144+
(get-buffer-process " *diff-hl* "))
145+
(process-live-p
146+
(get-buffer-process " *diff-hl-reference* ")))
147+
(accept-process-output nil 0.05))
147148

148149
(diff-hl-previous-hunk)
149150
(should (looking-at "added"))
@@ -165,17 +166,14 @@
165166
(save-buffer)
166167
(let ((diff-hl-show-staged-changes t))
167168
(should
168-
(equal (diff-hl-changes)
169-
'((:reference . nil)
170-
(:working
171-
.
172-
((1 1 0 insert)
173-
(12 1 0 insert)))))))
174-
(let ((diff-hl-show-staged-changes nil))
169+
(null
170+
(assoc-default :reference (diff-hl-changes)))))
171+
(let* ((diff-hl-show-staged-changes nil)
172+
(res-buf (assoc-default :reference (diff-hl-changes))))
175173
(should
176-
(equal (diff-hl-changes)
177-
'((:reference . ((1 1 0 insert)))
178-
(:working . ((12 1 0 insert)))))))))
174+
(equal
175+
(diff-hl-changes-from-buffer res-buf)
176+
'((1 1 0 insert)))))))
179177

180178
(diff-hl-deftest diff-hl-flydiff-can-ignore-staged-changes ()
181179
(diff-hl-test-in-source

0 commit comments

Comments
 (0)