@@ -160,31 +160,55 @@ is a zero-based file offset, assuming ‘utf-8-unix’ coding."
160160 (string-to-number (match-string 3 line)))))
161161 (concat (match-string 1 line) " :" (match-string 1 line)))))
162162
163- (defun clang-format--vc-diff-compute -diff-and-get- lines (buf -orig buf-cur )
163+ (defun clang-format--vc-diff-get -diff-lines (file -orig file-new )
164164 " Return all line regions that contain diffs between FILE-ORIG and
165165FILE-NEW. If there is no diff ‘nil’ is returned. Otherwise the
166166return is a ‘list’ of lines in the format ‘--lines=<start>:<end>’
167167which can be passed directly to ‘clang-format’."
168168 ; ; Use temporary buffer for output of diff.
169169 (with-temp-buffer
170- (diff-no-select buf-orig buf-cur " -a -U0" t (current-buffer ))
171- (let ((diff-lines '()))
172- ; ; Iterate through all lines in diff buffer and collect all
173- ; ; lines in current buffer that have a diff.
174- (goto-char (point-min ))
175- (while (not (eobp ))
176- (let ((diff-line (clang-format--vc-diff-match-diff-line
170+ (let ((status (call-process
171+ " diff"
172+ nil
173+ (current-buffer )
174+ nil
175+ ; ; Binary diff has different behaviors that we
176+ ; ; aren't interested in.
177+ " -a"
178+ ; ; Get minimal diff (copy diff config for git-clang-format).
179+ " -U0"
180+ file-orig
181+ file-new))
182+ (stderr (concat (if (zerop (buffer-size )) " " " : " )
177183 (buffer-substring-no-properties
178- (line-beginning-position )
179- (line-end-position )))))
180- (when diff-line
181- ; ; Create list line regions with diffs to pass to
182- ; ; clang-format.
183- (push (concat " --lines=" diff-line) diff-lines)))
184- (forward-line 1 ))
185- (reverse diff-lines))))
186-
187- (defun clang-format--vc-diff-get-diff-lines ()
184+ (point-min ) (line-end-position ))))
185+ (diff-lines '()))
186+ (cond
187+ ((stringp status)
188+ (error " (diff killed by signal %s%s ) " status stderr))
189+ ; ; Return of 0 indicates no diff.
190+ ((= status 0 ) nil )
191+ ; ; Return of 1 indicates found diffs and no error.
192+ ((= status 1 )
193+ ; ; Iterate through all lines in diff buffer and collect all
194+ ; ; lines in current buffer that have a diff.
195+ (goto-char (point-min ))
196+ (while (not (eobp ))
197+ (let ((diff-line (clang-format--vc-diff-match-diff-line
198+ (buffer-substring-no-properties
199+ (line-beginning-position )
200+ (line-end-position )))))
201+ (when diff-line
202+ ; ; Create list line regions with diffs to pass to
203+ ; ; clang-format.
204+ (push (concat " --lines=" diff-line) diff-lines)))
205+ (forward-line 1 ))
206+ (reverse diff-lines))
207+ ; ; Any return != 0 && != 1 indicates some level of error.
208+ (t
209+ (error " (diff returned unsuccessfully %s%s ) " status stderr))))))
210+
211+ (defun clang-format--vc-diff-get-vc-head-file (tmpfile-vc-head )
188212 " Stores the contents of ‘buffer-file-name’ at vc revision HEAD into
189213‘tmpfile-vc-head’. If the current buffer is either not a file or not
190214in a vc repo, this results in an error. Currently git is the only
@@ -193,44 +217,39 @@ supported vc."
193217 (unless (buffer-file-name )
194218 (error " Buffer is not visiting a file " ))
195219
196- (let ((inp-buf (current-buffer ))
197- (base-dir (vc-root-dir ))
220+ (let ((base-dir (vc-root-dir ))
198221 (backend (vc-backend (buffer-file-name ))))
199- (with-temp-buffer
200- ; ; We need to be able to find version control (git) root.
201- (unless base-dir
202- (error " File not known to version control system " ))
203- (cond
204- ((string-equal backend " Git" )
205- ; ; Get the filename relative to git root.
206- (let ((vc-file-name (substring
207- (expand-file-name (buffer-file-name inp-buf))
208- (string-width (expand-file-name base-dir))
209- nil )))
210- (let ((status (call-process
211- " git"
212- nil
213- (current-buffer )
214- nil
215- " show" (concat " HEAD:" vc-file-name)))
216- (stderr (concat (if (zerop (buffer-size )) " " " : " )
217- (buffer-substring-no-properties
218- (point-min ) (line-end-position )))))
219- (when (stringp status)
220- (error " (git show HEAD:%s killed by signal %s%s ) "
221- vc-file-name status stderr))
222- (unless (zerop status)
223- (error " (git show HEAD:%s returned unsuccessfully %s%s ) "
224- vc-file-name status stderr)))))
225- (t
226- (error
227- " Version control %s isn't supported, currently supported backends: git"
228- backend)))
229- ; ; Collect all lines where inp-buf (buffer we call
230- ; ; clang-format-vc-diff on) differs from latest version of the
231- ; ; backing file in the version control system.
232- (clang-format--vc-diff-compute-diff-and-get-lines
233- (current-buffer ) inp-buf))))
222+ ; ; We need to be able to find version control (git) root.
223+ (unless base-dir
224+ (error " File not known to git " ))
225+ (cond
226+ ((string-equal backend " Git" )
227+ ; ; Get the filename relative to git root.
228+ (let ((vc-file-name (substring
229+ (expand-file-name (buffer-file-name ))
230+ (string-width (expand-file-name base-dir))
231+ nil )))
232+ (let ((status (call-process
233+ " git"
234+ nil
235+ `(:file , tmpfile-vc-head)
236+ nil
237+ " show" (concat " HEAD:" vc-file-name)))
238+ (stderr (with-temp-buffer
239+ (unless (zerop (cadr (insert-file-contents tmpfile-vc-head)))
240+ (insert " : " ))
241+ (buffer-substring-no-properties
242+ (point-min ) (line-end-position )))))
243+ (when (stringp status)
244+ (error " (git show HEAD:%s killed by signal %s%s ) "
245+ vc-file-name status stderr))
246+ (unless (zerop status)
247+ (error " (git show HEAD:%s returned unsuccessfully %s%s ) "
248+ vc-file-name status stderr)))))
249+ (t
250+ (error
251+ " Version control %s isn't supported, currently supported backends: git"
252+ backend)))))
234253
235254
236255(defun clang-format--region-impl (start end &optional style assume-file-name lines )
@@ -304,6 +323,7 @@ specific locations for reformatting (i.e diff locations)."
304323 (delete-file temp-file)
305324 (when (buffer-name temp-buffer) (kill-buffer temp-buffer)))))
306325
326+
307327;;;### autoload
308328(defun clang-format-vc-diff (&optional style assume-file-name )
309329 " The same as ‘clang-format-buffer’ but only operates on the vc
@@ -312,15 +332,35 @@ diffs from HEAD in the buffer. If no STYLE is given uses
312332file. If no ASSUME-FILE-NAME is given uses the function
313333‘buffer-file-name’."
314334 (interactive )
315- (let ((diff-lines (clang-format--vc-diff-get-diff-lines)))
316- ; ; If we have any diffs, format them.
317- (when diff-lines
318- (clang-format--region-impl
319- (point-min )
320- (point-max )
321- style
322- assume-file-name
323- diff-lines))))
335+ (let ((tmpfile-vc-head nil )
336+ (tmpfile-curbuf nil ))
337+ (unwind-protect
338+ (progn
339+ (setq tmpfile-vc-head
340+ (make-temp-file " clang-format-vc-tmp-head-content" ))
341+ (clang-format--vc-diff-get-vc-head-file tmpfile-vc-head)
342+ ; ; Move the current buffer to a temporary file to take a
343+ ; ; diff. Even if current-buffer is backed by a file, we
344+ ; ; want to diff the buffer contents which might not be
345+ ; ; saved.
346+ (setq tmpfile-curbuf (make-temp-file " clang-format-vc-tmp" ))
347+ (write-region nil nil tmpfile-curbuf nil 'nomessage )
348+ ; ; Get a list of lines with a diff.
349+ (let ((diff-lines
350+ (clang-format--vc-diff-get-diff-lines
351+ tmpfile-vc-head tmpfile-curbuf)))
352+ ; ; If we have any diffs, format them.
353+ (when diff-lines
354+ (clang-format--region-impl
355+ (point-min )
356+ (point-max )
357+ style
358+ assume-file-name
359+ diff-lines))))
360+ (progn
361+ ; ; Cleanup temporary files we created.
362+ (when tmpfile-vc-head (delete-file tmpfile-vc-head))
363+ (when tmpfile-curbuf (delete-file tmpfile-curbuf))))))
324364
325365
326366;;;### autoload
0 commit comments