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 (incomplete-format (xml-get-attribute xml-node 'incomplete_format)) 65 replacements 66 cursor) 67 (dolist (node nodes) 68 (when (listp node) 69 (let* ((children (xml-node-children node)) 70 (text (car children))) 71 (cl-case (xml-node-name node) 72 ('replacement 73 (let* ((offset (xml-get-attribute-or-nil node 'offset)) 74 (length (xml-get-attribute-or-nil node 'length))) 75 (when (or (null offset) (null length)) 76 (error "<replacement> node does not have offset and length attributes")) 77 (when (cdr children) 78 (error "More than one child node in <replacement> node")) 79 80 (setq offset (string-to-number offset)) 81 (setq length (string-to-number length)) 82 (push (list offset length text) replacements))) 83 ('cursor 84 (setq cursor (string-to-number text))))))) 85 86 ;; Sort by decreasing offset, length. 87 (setq replacements (sort (delq nil replacements) 88 (lambda (a b) 89 (or (> (car a) (car b)) 90 (and (= (car a) (car b)) 91 (> (cadr a) (cadr b))))))) 92 93 (list replacements cursor (string= incomplete-format "true")))) 94 95(defun clang-format--replace (offset length &optional text) 96 (let ((start (byte-to-position (1+ offset))) 97 (end (byte-to-position (+ 1 offset length)))) 98 (goto-char start) 99 (delete-region start end) 100 (when text 101 (insert text)))) 102 103;;;###autoload 104(defun clang-format-region (char-start char-end &optional style) 105 "Use clang-format to format the code between START and END according to STYLE. 106If called interactively uses the region or the current statement if there 107is no active region. If no style is given uses `clang-format-style'." 108 (interactive 109 (if (use-region-p) 110 (list (region-beginning) (region-end)) 111 (list (point) (point)))) 112 113 (unless style 114 (setq style clang-format-style)) 115 116 (let ((start (1- (position-bytes char-start))) 117 (end (1- (position-bytes char-end))) 118 (cursor (1- (position-bytes (point)))) 119 (temp-buffer (generate-new-buffer " *clang-format-temp*")) 120 (temp-file (make-temp-file "clang-format"))) 121 (unwind-protect 122 (let (status stderr operations) 123 (setq status 124 (call-process-region 125 (point-min) (point-max) clang-format-executable 126 nil `(,temp-buffer ,temp-file) nil 127 128 "-output-replacements-xml" 129 "-assume-filename" (or (buffer-file-name) "") 130 "-style" style 131 "-offset" (number-to-string start) 132 "-length" (number-to-string (- end start)) 133 "-cursor" (number-to-string cursor))) 134 (setq stderr 135 (with-temp-buffer 136 (insert-file-contents temp-file) 137 (when (> (point-max) (point-min)) 138 (insert ": ")) 139 (buffer-substring-no-properties 140 (point-min) (line-end-position)))) 141 142 (cond 143 ((stringp status) 144 (error "(clang-format killed by signal %s%s)" status stderr)) 145 ((not (equal 0 status)) 146 (error "(clang-format failed with code %d%s)" status stderr))) 147 148 (with-current-buffer temp-buffer 149 (setq operations (clang-format--extract (car (xml-parse-region))))) 150 151 (let ((replacements (nth 0 operations)) 152 (cursor (nth 1 operations)) 153 (incomplete-format (nth 2 operations))) 154 (save-excursion 155 (mapc (lambda (rpl) 156 (apply #'clang-format--replace rpl)) 157 replacements)) 158 (when cursor 159 (goto-char (byte-to-position (1+ cursor)))) 160 (message "%s" incomplete-format) 161 (if incomplete-format 162 (message "(clang-format: incomplete (syntax errors)%s)" stderr) 163 (message "(clang-format: success%s)" stderr)))) 164 (delete-file temp-file) 165 (when (buffer-name temp-buffer) (kill-buffer temp-buffer))))) 166 167;;;###autoload 168(defun clang-format-buffer (&optional style) 169 "Use clang-format to format the current buffer according to STYLE." 170 (interactive) 171 (clang-format-region (point-min) (point-max) style)) 172 173;;;###autoload 174(defalias 'clang-format 'clang-format-region) 175 176(provide 'clang-format) 177;;; clang-format.el ends here 178