[clang] [clang-format] New clang-format-indent-mode for Emacs (PR #78904)

Amy Grinn via cfe-commits cfe-commits at lists.llvm.org
Sun Jan 21 08:26:59 PST 2024


https://github.com/amygrinn created https://github.com/llvm/llvm-project/pull/78904

This Emacs minor mode merges functionality from existing indentation functions such as 'c-indent-line' or 'treesit-indent' with the external clang-format utility.

On non-blank lines (lines with something other than whitespace), clang format will be used. Some replacements are filtered out if they affect text beyond the end of the current line.

This is useful, for example, for applying .clang-format rules such as IfMacros or AlignConsecutive* as you type.

Best,

Amy

>From eb6797aa84f9c8939a2b43c5bbd198a98ff2129a Mon Sep 17 00:00:00 2001
From: Amy Grinn <grinn.amy at gmail.com>
Date: Sun, 21 Jan 2024 10:16:18 -0500
Subject: [PATCH] [clang-format] New clang-format-indent-mode for Emacs

---
 clang/tools/clang-format/clang-format.el | 53 ++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/clang/tools/clang-format/clang-format.el b/clang/tools/clang-format/clang-format.el
index f43bf063c62970..510ceb7ef93bb3 100644
--- a/clang/tools/clang-format/clang-format.el
+++ b/clang/tools/clang-format/clang-format.el
@@ -28,6 +28,13 @@
 ;;
 ;;   (global-set-key [C-M-tab] 'clang-format-region)
 
+;; clang-format.el comes with a local minor mode
+;; `clang-format-indent-mode'.  This mode advises the existing
+;; indentation function using the `clang-format-executable'. This can
+;; be turned on automatically using a mode hook:
+;;
+;;   (add-hook 'c-mode-common-hook 'clang-format-indent-mode)
+
 ;;; Code:
 
 (require 'cl-lib)
@@ -217,5 +224,51 @@ the function `buffer-file-name'."
 ;;;###autoload
 (defalias 'clang-format 'clang-format-region)
 
+(defun clang-format--indent-line (&rest _)
+  "Indent current line using clang-format, if not on a blank line.
+
+Returns nil on blank lines. Discards replacements which
+affect text beyond the end of the line."
+  (when (save-excursion
+          (beginning-of-line)
+          (not (looking-at-p "[ \t]*$")))
+    (prog1 t
+      ;; Remove trailing whitespace
+      (save-excursion
+        (end-of-line)
+        (delete-char (- (skip-chars-backward " \t"))))
+      ;; Discard replacements which affect text beyond end of line
+      (let* ((next-char (clang-format--bufferpos-to-filepos
+                         (save-excursion
+                           (end-of-line)
+                           (skip-chars-forward " \t\r\n")
+                           (point))
+                         'exact 'utf-8-unix))
+             (advice `(lambda (offset length &rest _)
+                        (< (+ offset length) ,next-char))))
+        (advice-add #'clang-format--replace :before-while advice)
+        (unwind-protect
+            (let ((inhibit-message t))
+              (clang-format-region (line-beginning-position)
+                                   (line-end-position)))
+          (advice-remove #'clang-format--replace advice)))
+      (when (< (current-column) (current-indentation))
+        (back-to-indentation)))))
+
+;;;###autoload
+(define-minor-mode clang-format-indent-mode
+  "Use clang-format to control indentation on contentful lines.
+
+Clang format indent mode is a buffer-local minor mode. When
+enabled, indentation for lines that do not solely consist of
+whitespace will be determined by running the buffer through the
+`clang-format-executable' program. On empty lines, the existing
+indentation function will be used."
+  :global nil :group 'tools
+  (if clang-format-indent-mode
+      (advice-add indent-line-function :before-until
+                  #'clang-format--indent-line)
+    (advice-remove indent-line-function #'clang-format--indent-line)))
+
 (provide 'clang-format)
 ;;; clang-format.el ends here



More information about the cfe-commits mailing list