xref: /aosp_15_r20/external/clang/tools/clang-format/clang-format.el (revision 67e74705e28f6214e480b399dd47ea732279e315)
1*67e74705SXin Li;;; clang-format.el --- Format code using clang-format
2*67e74705SXin Li
3*67e74705SXin Li;; Keywords: tools, c
4*67e74705SXin Li;; Package-Requires: ((cl-lib "0.3"))
5*67e74705SXin Li
6*67e74705SXin Li;;; Commentary:
7*67e74705SXin Li
8*67e74705SXin Li;; This package allows to filter code through clang-format to fix its formatting.
9*67e74705SXin Li;; clang-format is a tool that formats C/C++/Obj-C code according to a set of
10*67e74705SXin Li;; style options, see <http://clang.llvm.org/docs/ClangFormatStyleOptions.html>.
11*67e74705SXin Li;; Note that clang-format 3.4 or newer is required.
12*67e74705SXin Li
13*67e74705SXin Li;; clang-format.el is available via MELPA and can be installed via
14*67e74705SXin Li;;
15*67e74705SXin Li;;   M-x package-install clang-format
16*67e74705SXin Li;;
17*67e74705SXin Li;; when ("melpa" . "http://melpa.org/packages/") is included in
18*67e74705SXin Li;; `package-archives'. Alternatively, ensure the directory of this
19*67e74705SXin Li;; file is in your `load-path' and add
20*67e74705SXin Li;;
21*67e74705SXin Li;;   (require 'clang-format)
22*67e74705SXin Li;;
23*67e74705SXin Li;; to your .emacs configuration.
24*67e74705SXin Li
25*67e74705SXin Li;; You may also want to bind `clang-format-region' to a key:
26*67e74705SXin Li;;
27*67e74705SXin Li;;   (global-set-key [C-M-tab] 'clang-format-region)
28*67e74705SXin Li
29*67e74705SXin Li;;; Code:
30*67e74705SXin Li
31*67e74705SXin Li(require 'cl-lib)
32*67e74705SXin Li(require 'xml)
33*67e74705SXin Li
34*67e74705SXin Li(defgroup clang-format nil
35*67e74705SXin Li  "Format code using clang-format."
36*67e74705SXin Li  :group 'tools)
37*67e74705SXin Li
38*67e74705SXin Li(defcustom clang-format-executable
39*67e74705SXin Li  (or (executable-find "clang-format")
40*67e74705SXin Li      "clang-format")
41*67e74705SXin Li  "Location of the clang-format executable.
42*67e74705SXin Li
43*67e74705SXin LiA string containing the name or the full path of the executable."
44*67e74705SXin Li  :group 'clang-format
45*67e74705SXin Li  :type 'string
46*67e74705SXin Li  :risky t)
47*67e74705SXin Li
48*67e74705SXin Li(defcustom clang-format-style "file"
49*67e74705SXin Li  "Style argument to pass to clang-format.
50*67e74705SXin Li
51*67e74705SXin LiBy default clang-format will load the style configuration from
52*67e74705SXin Lia file named .clang-format located in one of the parent directories
53*67e74705SXin Liof the buffer."
54*67e74705SXin Li  :group 'clang-format
55*67e74705SXin Li  :type 'string
56*67e74705SXin Li  :safe #'stringp)
57*67e74705SXin Li(make-variable-buffer-local 'clang-format-style)
58*67e74705SXin Li
59*67e74705SXin Li(defun clang-format--extract (xml-node)
60*67e74705SXin Li  "Extract replacements and cursor information from XML-NODE."
61*67e74705SXin Li  (unless (and (listp xml-node) (eq (xml-node-name xml-node) 'replacements))
62*67e74705SXin Li    (error "Expected <replacements> node"))
63*67e74705SXin Li  (let ((nodes (xml-node-children xml-node))
64*67e74705SXin Li        (incomplete-format (xml-get-attribute xml-node 'incomplete_format))
65*67e74705SXin Li        replacements
66*67e74705SXin Li        cursor)
67*67e74705SXin Li    (dolist (node nodes)
68*67e74705SXin Li      (when (listp node)
69*67e74705SXin Li        (let* ((children (xml-node-children node))
70*67e74705SXin Li               (text (car children)))
71*67e74705SXin Li          (cl-case (xml-node-name node)
72*67e74705SXin Li            ('replacement
73*67e74705SXin Li             (let* ((offset (xml-get-attribute-or-nil node 'offset))
74*67e74705SXin Li                    (length (xml-get-attribute-or-nil node 'length)))
75*67e74705SXin Li               (when (or (null offset) (null length))
76*67e74705SXin Li                 (error "<replacement> node does not have offset and length attributes"))
77*67e74705SXin Li               (when (cdr children)
78*67e74705SXin Li                 (error "More than one child node in <replacement> node"))
79*67e74705SXin Li
80*67e74705SXin Li               (setq offset (string-to-number offset))
81*67e74705SXin Li               (setq length (string-to-number length))
82*67e74705SXin Li               (push (list offset length text) replacements)))
83*67e74705SXin Li            ('cursor
84*67e74705SXin Li             (setq cursor (string-to-number text)))))))
85*67e74705SXin Li
86*67e74705SXin Li    ;; Sort by decreasing offset, length.
87*67e74705SXin Li    (setq replacements (sort (delq nil replacements)
88*67e74705SXin Li                             (lambda (a b)
89*67e74705SXin Li                               (or (> (car a) (car b))
90*67e74705SXin Li                                   (and (= (car a) (car b))
91*67e74705SXin Li                                        (> (cadr a) (cadr b)))))))
92*67e74705SXin Li
93*67e74705SXin Li    (list replacements cursor (string= incomplete-format "true"))))
94*67e74705SXin Li
95*67e74705SXin Li(defun clang-format--replace (offset length &optional text)
96*67e74705SXin Li  (let ((start (byte-to-position (1+ offset)))
97*67e74705SXin Li        (end (byte-to-position (+ 1 offset length))))
98*67e74705SXin Li    (goto-char start)
99*67e74705SXin Li    (delete-region start end)
100*67e74705SXin Li    (when text
101*67e74705SXin Li      (insert text))))
102*67e74705SXin Li
103*67e74705SXin Li;;;###autoload
104*67e74705SXin Li(defun clang-format-region (char-start char-end &optional style)
105*67e74705SXin Li  "Use clang-format to format the code between START and END according to STYLE.
106*67e74705SXin LiIf called interactively uses the region or the current statement if there
107*67e74705SXin Liis no active region.  If no style is given uses `clang-format-style'."
108*67e74705SXin Li  (interactive
109*67e74705SXin Li   (if (use-region-p)
110*67e74705SXin Li       (list (region-beginning) (region-end))
111*67e74705SXin Li     (list (point) (point))))
112*67e74705SXin Li
113*67e74705SXin Li  (unless style
114*67e74705SXin Li    (setq style clang-format-style))
115*67e74705SXin Li
116*67e74705SXin Li  (let ((start (1- (position-bytes char-start)))
117*67e74705SXin Li        (end (1- (position-bytes char-end)))
118*67e74705SXin Li        (cursor (1- (position-bytes (point))))
119*67e74705SXin Li        (temp-buffer (generate-new-buffer " *clang-format-temp*"))
120*67e74705SXin Li        (temp-file (make-temp-file "clang-format")))
121*67e74705SXin Li    (unwind-protect
122*67e74705SXin Li        (let (status stderr operations)
123*67e74705SXin Li          (setq status
124*67e74705SXin Li                (call-process-region
125*67e74705SXin Li                 (point-min) (point-max) clang-format-executable
126*67e74705SXin Li                 nil `(,temp-buffer ,temp-file) nil
127*67e74705SXin Li
128*67e74705SXin Li                 "-output-replacements-xml"
129*67e74705SXin Li                 "-assume-filename" (or (buffer-file-name) "")
130*67e74705SXin Li                 "-style" style
131*67e74705SXin Li                 "-offset" (number-to-string start)
132*67e74705SXin Li                 "-length" (number-to-string (- end start))
133*67e74705SXin Li                 "-cursor" (number-to-string cursor)))
134*67e74705SXin Li          (setq stderr
135*67e74705SXin Li                (with-temp-buffer
136*67e74705SXin Li                  (insert-file-contents temp-file)
137*67e74705SXin Li                  (when (> (point-max) (point-min))
138*67e74705SXin Li                    (insert ": "))
139*67e74705SXin Li                  (buffer-substring-no-properties
140*67e74705SXin Li                   (point-min) (line-end-position))))
141*67e74705SXin Li
142*67e74705SXin Li          (cond
143*67e74705SXin Li           ((stringp status)
144*67e74705SXin Li            (error "(clang-format killed by signal %s%s)" status stderr))
145*67e74705SXin Li           ((not (equal 0 status))
146*67e74705SXin Li            (error "(clang-format failed with code %d%s)" status stderr)))
147*67e74705SXin Li
148*67e74705SXin Li          (with-current-buffer temp-buffer
149*67e74705SXin Li            (setq operations (clang-format--extract (car (xml-parse-region)))))
150*67e74705SXin Li
151*67e74705SXin Li          (let ((replacements (nth 0 operations))
152*67e74705SXin Li                (cursor (nth 1 operations))
153*67e74705SXin Li                (incomplete-format (nth 2 operations)))
154*67e74705SXin Li            (save-excursion
155*67e74705SXin Li              (mapc (lambda (rpl)
156*67e74705SXin Li                      (apply #'clang-format--replace rpl))
157*67e74705SXin Li                    replacements))
158*67e74705SXin Li            (when cursor
159*67e74705SXin Li              (goto-char (byte-to-position (1+ cursor))))
160*67e74705SXin Li            (message "%s" incomplete-format)
161*67e74705SXin Li            (if incomplete-format
162*67e74705SXin Li                (message "(clang-format: incomplete (syntax errors)%s)" stderr)
163*67e74705SXin Li              (message "(clang-format: success%s)" stderr))))
164*67e74705SXin Li      (delete-file temp-file)
165*67e74705SXin Li      (when (buffer-name temp-buffer) (kill-buffer temp-buffer)))))
166*67e74705SXin Li
167*67e74705SXin Li;;;###autoload
168*67e74705SXin Li(defun clang-format-buffer (&optional style)
169*67e74705SXin Li  "Use clang-format to format the current buffer according to STYLE."
170*67e74705SXin Li  (interactive)
171*67e74705SXin Li  (clang-format-region (point-min) (point-max) style))
172*67e74705SXin Li
173*67e74705SXin Li;;;###autoload
174*67e74705SXin Li(defalias 'clang-format 'clang-format-region)
175*67e74705SXin Li
176*67e74705SXin Li(provide 'clang-format)
177*67e74705SXin Li;;; clang-format.el ends here
178