3737
3838(defvar diff-hl-dired-process-buffer nil )
3939
40+ (defvar diff-hl-dired--status nil
41+ " Status of the VC dir-status operation.
42+
43+ State transitions (no concurrent requests):
44+ nil - (request) -> running -- start VC dir-status-files
45+ running - (done) -> nil -- VC finished
46+
47+ State transitions (concurrent requests):
48+ running - (request) -> pending -- VC busy, queue retry
49+ pending - (request) -> pending -- already queued, ignore
50+ pending - (done) -> nil -- VC finished, schedule retry" )
51+
4052(defgroup diff-hl-dired nil
4153 " VC diff highlighting on the side of a Dired window."
4254 :group 'diff-hl )
5971
6072(defface diff-hl-dired-ignored
6173 '((default :inherit dired-ignored ))
62- " Face used to highlight unregistered files." )
74+ " Face used to highlight ignored files." )
6375
6476(defcustom diff-hl-dired-extra-indicators t
6577 " Non-nil to indicate ignored files."
@@ -86,6 +98,7 @@ status indicators."
8698 (progn
8799 (diff-hl-maybe-define-bitmaps)
88100 (set (make-local-variable 'diff-hl-dired-process-buffer ) nil )
101+ (set (make-local-variable 'diff-hl-dired--status ) nil )
89102 (add-hook 'dired-after-readin-hook 'diff-hl-dired-update nil t ))
90103 (remove-hook 'dired-after-readin-hook 'diff-hl-dired-update t )
91104 (diff-hl-dired-clear)))
@@ -95,66 +108,95 @@ status indicators."
95108 (let ((backend (ignore-errors (vc-responsible-backend default-directory)))
96109 (def-dir default-directory)
97110 (buffer (current-buffer ))
111+ (state-to-type '( edited change
112+ added insert
113+ removed delete
114+ unregistered unknown
115+ ignored ignored))
98116 dirs-alist files-alist)
99117 (when (and backend (not (memq backend diff-hl-dired-ignored-backends)))
100- (diff-hl-dired-clear)
101- (if (buffer-live-p diff-hl-dired-process-buffer)
102- (let ((proc (get-buffer-process diff-hl-dired-process-buffer)))
103- (when proc (kill-process proc)))
104- (setq diff-hl-dired-process-buffer
105- (generate-new-buffer " *diff-hl-dired* tmp status" )))
106- (with-current-buffer diff-hl-dired-process-buffer
107- (setq default-directory (expand-file-name def-dir))
108- (erase-buffer )
109- (diff-hl-dired-status-files
110- backend def-dir
111- (when diff-hl-dired-extra-indicators
112- (cl-loop for file in (directory-files def-dir)
113- unless (member file '(" ." " .." " .hg" ))
114- collect file))
115- (lambda (entries &optional more-to-come )
116- (when (buffer-live-p buffer)
117- (with-current-buffer buffer
118- (dolist (entry entries)
119- (cl-destructuring-bind (file state &rest r) entry
120- ; ; Work around http://debbugs.gnu.org/18605
121- (setq file (replace-regexp-in-string " \\ ` " " " file))
122- (let ((type (plist-get
123- '( edited change added insert removed delete
124- unregistered unknown ignored ignored)
125- state)))
126- (if (string-match " \\ `\\ ([^/]+\\ )/" file)
127- (let* ((dir (match-string 1 file))
128- (value (cdr (assoc dir dirs-alist))))
129- (unless (eq value type)
130- (cond
131- ((eq state 'up-to-date ))
132- ((null value)
133- (push (cons dir type) dirs-alist))
134- ((not (eq type 'ignored ))
135- (setcdr (assoc dir dirs-alist) 'change )))))
136- (push (cons file type) files-alist)))))
137- (unless more-to-come
138- (diff-hl-dired-highlight-items
139- (append dirs-alist files-alist))))
140- (unless more-to-come
141- (kill-buffer diff-hl-dired-process-buffer))))
118+ (pcase diff-hl-dired--status
119+ ; ; running -> pending
120+ ('running (setq diff-hl-dired--status 'pending ))
121+ ; ; pending -> pending, already queued, ignore
122+ ('pending nil )
123+ ; ; nil -> running
124+ (_
125+ (setq diff-hl-dired--status 'running )
126+ (unless (buffer-live-p diff-hl-dired-process-buffer)
127+ (setq diff-hl-dired-process-buffer
128+ (generate-new-buffer " *diff-hl-dired* tmp status" )))
129+ (with-current-buffer diff-hl-dired-process-buffer
130+ (setq default-directory (expand-file-name def-dir))
131+ (erase-buffer )
132+ (let ((files
133+ (when diff-hl-dired-extra-indicators
134+ (cl-loop for file in (directory-files def-dir)
135+ unless (member file '(" ." " .." " .hg" ))
136+ collect file)))
137+ (update-fn
138+ (lambda (entries &optional more-to-come )
139+ (when (buffer-live-p buffer)
140+ (with-current-buffer buffer
141+ (dolist (entry entries)
142+ (cl-destructuring-bind (file state &rest r) entry
143+ ; ; Work around http://debbugs.gnu.org/18605
144+ (setq file (replace-regexp-in-string " \\ ` " " " file))
145+ (let ((type (plist-get state-to-type state))
146+ (dirs (cl-loop with pos = 0
147+ while (string-match " /" file pos)
148+ do (setq pos (match-end 0 ))
149+ collect (substring file 0 (1- pos)))))
150+ (dolist (dir dirs)
151+ (let ((value (cdr (assoc dir dirs-alist))))
152+ (cond
153+ ((eq value type)) ; ; skip
154+ ((eq state 'up-to-date )) ; ; skip
155+ ((null value)
156+ (push (cons dir type) dirs-alist))
157+ ((not (eq type 'ignored ))
158+ (setcdr (assoc dir dirs-alist) 'change )))))
159+ (push (cons file type) files-alist)
160+ )))
161+ (unless more-to-come
162+ (diff-hl-dired-highlight-items
163+ (append dirs-alist files-alist))
164+ (pcase diff-hl-dired--status
165+ ; ; running -> nil
166+ ('running
167+ (setq diff-hl-dired--status nil ))
168+ ; ; pending -> nil, schedule retry
169+ ('pending
170+ (setq diff-hl-dired--status nil )
171+ (run-at-time 0 nil
172+ (lambda ()
173+ (when (buffer-live-p buffer)
174+ (with-current-buffer buffer
175+ (diff-hl-dired-update))))))))))
176+ (unless more-to-come
177+ (kill-buffer diff-hl-dired-process-buffer)))))
178+ (diff-hl-dired-status-files backend def-dir files update-fn)))
142179 )))))
143180
144181(defun diff-hl-dired-status-files (backend dir files update-function )
145182 " Using version control BACKEND, return list of (FILE STATE EXTRA) entries
146183for DIR containing FILES. Call UPDATE-FUNCTION as entries are added."
147- (vc-call-backend backend 'dir-status-files dir files update-function))
184+ (vc-call-backend backend 'dir-status-files
185+ dir files update-function))
148186
149187(defun diff-hl-dired-highlight-items (alist )
150188 " Highlight ALIST containing (FILE . TYPE) elements."
189+ ; ; clear overlays right before drawing to avoid flicker
190+ (diff-hl-dired-clear)
151191 (dolist (pair alist)
152192 (let ((file (car pair))
153193 (type (cdr pair)))
154194 (save-excursion
155195 (goto-char (point-min ))
156- (when (and type (dired-goto-file-1
157- file (expand-file-name file) nil ))
196+ (when (and type
197+ (dired-goto-file-1
198+ (file-name-nondirectory file) ; ; basename
199+ (expand-file-name file) nil ))
158200 (let* ((diff-hl-fringe-bmp-function diff-hl-dired-fringe-bmp-function)
159201 (diff-hl-fringe-face-function 'diff-hl-dired-face-from-type )
160202 (o (diff-hl-add-highlighting type 'single )))
0 commit comments