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