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