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))) lines) (with-current-buffer use-buf (save-excursion (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))))))) lines)) (defun clang-format-vc-diff () "Reformat only the changed lines from VC in the buffer" (interactive) (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) 'working-revision buffer-file-name)) (temp-buffer (generate-new-buffer " *clang-format-temp*")) (temp-file (make-temp-file "clang-format")) diff-line-args head-temp-file) (unwind-protect (if (not head-revision) (progn (message "(clang-format-vc-diff: %s not revision controlled, formatting whole buffer" buffer-file-name) (clang-format-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 "-u0" head-temp-file "-")) (stderr (with-temp-buffer (unless (zerop (cadr (insert-file-contents temp-file))) (insert ": ")) (buffer-substring-no-properties (point-min) (line-end-position))))) (cond ((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 `("-output-replacements-xml" "-assume-filename" ,(file-name-nondirectory buffer-file-name) "-fallback-style" ,clang-format-fallback-style "-cursor" ,(number-to-string cursor) ,@diff-line-args))) (stderr (with-temp-buffer (unless (zerop (cadr (insert-file-contents temp-file))) (insert ": ")) (buffer-substring-no-properties (point-min) (line-end-position))))) (cond ((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)))) (save-excursion (dolist (rpl replacements) (apply #'clang-format--replace rpl))) (when cursor (goto-char (clang-format--filepos-to-bufferpos cursor 'exact 'utf-8-unix))) (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)))))