[clang] [emacs][clang-format] Add elisp API for clang-format on git diffs (PR #112792)

Campbell Barton via cfe-commits cfe-commits at lists.llvm.org
Sun Nov 3 14:38:41 PST 2024


================
@@ -146,18 +146,115 @@ is a zero-based file offset, assuming ‘utf-8-unix’ coding."
     (lambda (byte &optional _quality _coding-system)
       (byte-to-position (1+ byte)))))
 
-;;;###autoload
-(defun clang-format-region (start end &optional style assume-file-name)
-  "Use clang-format to format the code between START and END according to STYLE.
-If called interactively uses the region or the current statement if there is no
-no active region. If no STYLE is given uses `clang-format-style'. Use
-ASSUME-FILE-NAME to locate a style config file, if no ASSUME-FILE-NAME is given
-uses the function `buffer-file-name'."
-  (interactive
-   (if (use-region-p)
-       (list (region-beginning) (region-end))
-     (list (point) (point))))
-
+(defun clang-format--vc-diff-match-diff-line (line)
+  ;; Matching something like:
+  ;; "@@ -80 +80 @@" or "@@ -80,2 +80,2 @@"
+  ;; Return as "<LineStart>:<LineEnd>"
+  (when (string-match "^@@\s-[0-9,]+\s\\+\\([0-9]+\\)\\(,\\([0-9]+\\)\\)?\s@@$" line)
+    ;; If we have multi-line diff
+    (if (match-string 3 line)
+        (concat (match-string 1 line)
+                ":"
+                (number-to-string
+                 (+ (string-to-number (match-string 1 line))
+                    (string-to-number (match-string 3 line)))))
+      (concat (match-string 1 line) ":" (match-string 1 line)))))
+
+(defun clang-format--vc-diff-get-diff-lines (file-orig file-new)
+  "Return all line regions that contain diffs between FILE-ORIG and
+FILE-NEW.  If there is no diff 'nil' is returned. Otherwise the
+return is a 'list' of lines in the format '--lines=<start>:<end>'
+which can be passed directly to 'clang-format'"
+  ;; Temporary buffer for output of diff.
+  (with-temp-buffer
+    (let ((status (call-process
+                   "diff"
+                   nil
+                   (current-buffer)
+                   nil
+                   ;; Binary diff has different behaviors that we
+                   ;; aren't interested in.
+                   "-a"
+                   ;; Get minimal diff (copy diff config for git-clang-format)
+                   "-U0"
+                   file-orig
+                   file-new))
+          (stderr (concat (if (zerop (buffer-size)) "" ": ")
+                          (buffer-substring-no-properties
+                           (point-min) (line-end-position))))
+          (diff-lines '()))
+      (cond
+       ((stringp status)
+        (error "(diff killed by signal %s%s)" status stderr))
+       ;; Return of 0 indicates no diff
+       ((= status 0) nil)
+       ;; Return of 1 indicates found diffs and no error
+       ((= status 1)
+        ;; Iterate through all lines in diff buffer and collect all
+        ;; lines in current buffer that have a diff.
+        (goto-char (point-min))
+        (while (not (eobp))
+          (let ((diff-line (clang-format--vc-diff-match-diff-line
+                            (buffer-substring-no-properties
+                             (line-beginning-position)
+                             (line-end-position)))))
+            (when diff-line
+              ;; Create list line regions with diffs to pass to
+              ;; clang-format
+              (add-to-list 'diff-lines (concat "--lines=" diff-line) t)))
+          (forward-line 1))
+        diff-lines)
+       ;; Any return != 0 && != 1 indicates some level of error
+       (t
+        (error "(diff returned unsuccessfully %s%s)" status stderr))))))
+
+(defun clang-format--vc-diff-get-vc-head-file (tmpfile-vc-head)
+  "Stores the contents of 'buffer-file-name' at vc revision HEAD into
+'tmpfile-vc-head'. If the current buffer is either not a file or not
+in a vc repo, this results in an error. Currently git is the only
+supported vc."
+  ;; Needs current buffer to be a file
+  (unless (buffer-file-name)
+    (error "Buffer is not visiting a file"))
+  ;; Only version control currently supported is Git
+  (unless (string-equal (vc-backend (buffer-file-name)) "Git")
+    (error "Not using git"))
+
+  (let ((base-dir (vc-root-dir)))
+    ;; Need to be able to find version control (git) root
----------------
ideasman42 wrote:

\*picky\* use full sentences for comments - end with a full-stop, applies to many other comments here.

https://github.com/llvm/llvm-project/pull/112792


More information about the cfe-commits mailing list