Formatting the changed lines (VC) of C code buffer.

The following elisp function (and it's support functions) will clang-format the changed lines in buffer containing C/C++. If the function looks a bit long that's because it is being careful to not delete and replace many lines so that the point is not disturned as much as possible.

(defvar diffu-new-lines-re "^@@.*\\+\\([0-9]+\\)\\(,\\([0-9]+\\)\\)?")

(defun clang-format-args-from-diff-buffer (&optional buffer)
  (let ((use-buf (or buffer (current-buffer)))
    (with-current-buffer use-buf
	(goto-char (point-max))
	(while (re-search-backward diffu-new-lines-re nil 'noerror)
	  (let ((start (string-to-number (match-string 1)))
		(count (string-to-number (or (match-string 3) "1"))))
	    (unless (= count 0)
	      (setq lines (cons (format "--lines=%d:%d" start (+ start count -1)) lines)))))))

(defun clang-format-vc-diff ()
  "Reformat only the changed lines from VC in the buffer"
  (let* ((code-buffer (current-buffer))
	 (cursor (clang-format--bufferpos-to-filepos (point) 'exact 'utf-8-unix))
	 ;; vc-working-revision has some bizarre cache bug, it
	 ;; returns the revision that was HEAD the first time it is
	 ;; checked in a buffer. If that buffer is saved, and checked
	 ;; the next call to vc-working-revision is not updated, one
	 ;; must reload the file. So instead invoke the backend directly.
	 (head-revision (vc-call-backend (vc-backend buffer-file-name)
	 (temp-buffer (generate-new-buffer " *clang-format-temp*"))
	 (temp-file (make-temp-file "clang-format"))
	(if (not head-revision)
	      (message "(clang-format-vc-diff: %s not revision controlled, formatting whole buffer"
	  ;; Get the HEAD revision into a temp file
	  (let ((head-temp-buf (vc-find-revision buffer-file-name head-revision)))
	    (setq head-temp-file (buffer-file-name head-temp-buf))
	    (kill-buffer head-temp-buf))
	  ;; Get the diff of buffer from the HEAD revision
	  (let ((status (call-process-region
			 nil nil (executable-find "diff")
			 nil `(,temp-buffer ,temp-file) nil
		(stderr (with-temp-buffer
			  (unless (zerop (cadr (insert-file-contents temp-file)))
			    (insert ": "))
			   (point-min) (line-end-position)))))
	     ((stringp status)
	      (error "(clang-format-vc-diff diff killed by signal %s%s)" status stderr))
	     ((zerop status)
	      (message "(clang-format-vc-diff no diff"))
	     ((not (= 1 status))
	      (error "(clang-format-vc-diff diff failed with code %d%s)" status stderr))))
	  (setq diff-line-args (clang-format-args-from-diff-buffer temp-buffer))
	  ;; empty the temp buffer, and delete the temp file
	  (with-current-buffer temp-buffer (erase-buffer))
	  ;; Get xml-replacements into now-empty temp-buffer
	  (if (not diff-line-args)
	      (message "(clang-format-vc-diff no differences to format)")
	    (let ((status (apply #'call-process-region
				 nil nil clang-format-executable
				 nil `(,temp-buffer ,temp-file) nil
				   "-assume-filename" ,(file-name-nondirectory buffer-file-name)
				   "-fallback-style" ,clang-format-fallback-style
				   "-cursor" ,(number-to-string cursor)
		  (stderr (with-temp-buffer
			    (unless (zerop (cadr (insert-file-contents temp-file)))
			      (insert ": "))
			     (point-min) (line-end-position)))))
	       ((stringp status)
		(error "(clang-format killed by signal %s%s)" status stderr))
	       ((not (zerop status))
		(error "(clang-format failed with code %d%s)" status stderr)))
	      (if (= 0 (buffer-size temp-buffer))
		  (message "(vpp-clang-diff-format-buffer no formatting changes")
		(cl-destructuring-bind (replacements cursor incomplete-format)
		    (with-current-buffer temp-buffer
		      (clang-format--extract (car (xml-parse-region))))
		    (dolist (rpl replacements)
		      (apply #'clang-format--replace rpl)))
		  (when cursor
		    (goto-char (clang-format--filepos-to-bufferpos cursor 'exact
		  (if incomplete-format
		      (message "(clang-format: incomplete (syntax errors)%s)" stderr)
		    (message "(clang-format: success%s)" stderr)))))))
      (ignore-errors (kill-buffer temp-buffer))
      (ignore-errors (delete-file temp-file))
      (ignore-errors (delete-file head-temp-file)))))