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