summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorManuel Klimek <klimek@google.com>2015-01-13 08:35:34 +0000
committerManuel Klimek <klimek@google.com>2015-01-13 08:35:34 +0000
commit424535e64f91f87235451d059f68e8cd5687453d (patch)
tree44e8bdb752f512f0490198d147264328fc9f1eb1 /tools
parentf233cba7f0cdbdae7e3e40078cdb422c00be73c5 (diff)
Update clang-format.el to use xml output and patch in the returned chunks.
This leads to better undo behavior and avoids window content jumping around. Patch by Johann Klähn. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@225777 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools')
-rw-r--r--tools/clang-format/clang-format.el132
1 files changed, 86 insertions, 46 deletions
diff --git a/tools/clang-format/clang-format.el b/tools/clang-format/clang-format.el
index e6c0c3c9a4..ab0991b2df 100644
--- a/tools/clang-format/clang-format.el
+++ b/tools/clang-format/clang-format.el
@@ -1,7 +1,7 @@
;;; clang-format.el --- Format code using clang-format
;; Keywords: tools, c
-;; Package-Requires: ((json "1.3"))
+;; Package-Requires: ((cl-lib "0.3"))
;;; Commentary:
@@ -28,7 +28,8 @@
;;; Code:
-(require 'json)
+(require 'cl-lib)
+(require 'xml)
(defgroup clang-format nil
"Format code using clang-format."
@@ -55,6 +56,47 @@ of the buffer."
:safe #'stringp)
(make-variable-buffer-local 'clang-format-style)
+(defun clang-format--extract (xml-node)
+ "Extract replacements and cursor information from XML-NODE."
+ (unless (and (listp xml-node) (eq (xml-node-name xml-node) 'replacements))
+ (error "Expected <replacements> node"))
+ (let ((nodes (xml-node-children xml-node))
+ replacements
+ cursor)
+ (dolist (node nodes)
+ (when (listp node)
+ (let* ((children (xml-node-children node))
+ (text (car children)))
+ (cl-case (xml-node-name node)
+ ('replacement
+ (let* ((offset (xml-get-attribute-or-nil node 'offset))
+ (length (xml-get-attribute-or-nil node 'length)))
+ (when (or (null offset) (null length))
+ (error "<replacement> node does not have offset and length attributes"))
+ (when (cdr children)
+ (error "More than one child node in <replacement> node"))
+
+ (setq offset (1+ (string-to-number offset)))
+ (setq length (string-to-number length))
+ (push (list offset length text) replacements)))
+ ('cursor
+ (setq cursor (1+ (string-to-number text))))))))
+
+ ;; Sort by decreasing offset, length.
+ (setq replacements (sort (delq nil replacements)
+ (lambda (a b)
+ (or (> (car a) (car b))
+ (and (= (car a) (car b))
+ (> (cadr a) (cadr b)))))))
+
+ (cons replacements cursor)))
+
+(defun clang-format--replace (offset length &optional text)
+ (goto-char offset)
+ (delete-char length)
+ (when text
+ (insert text)))
+
;;;###autoload
(defun clang-format-region (start end &optional style)
"Use clang-format to format the code between START and END according to STYLE.
@@ -68,51 +110,49 @@ is no active region. If no style is given uses `clang-format-style'."
(unless style
(setq style clang-format-style))
- (let* ((temp-file (make-temp-file "clang-format"))
- (keep-stderr (list t temp-file))
- (window-starts
- (mapcar (lambda (w) (list w (window-start w)))
- (get-buffer-window-list)))
- (status)
- (stderr)
- (json))
-
+ (let ((temp-buffer (generate-new-buffer " *clang-format-temp*"))
+ (temp-file (make-temp-file "clang-format")))
(unwind-protect
- (setq status
- (call-process-region
- (point-min) (point-max) clang-format-executable
- 'delete keep-stderr nil
-
- "-assume-filename" (or (buffer-file-name) "")
- "-style" style
- "-offset" (number-to-string (1- start))
- "-length" (number-to-string (- end start))
- "-cursor" (number-to-string (1- (point))))
- stderr
- (with-temp-buffer
- (insert-file-contents temp-file)
- (when (> (point-max) (point-min))
- (insert ": "))
- (buffer-substring-no-properties
- (point-min) (line-end-position))))
- (delete-file temp-file))
-
- (cond
- ((stringp status)
- (error "(clang-format killed by signal %s%s)" status stderr))
- ((not (equal 0 status))
- (error "(clang-format failed with code %d%s)" status stderr))
- (t (message "(clang-format succeeded%s)" stderr)))
-
- (goto-char (point-min))
- (setq json (json-read-from-string
- (buffer-substring-no-properties
- (point-min) (line-end-position))))
-
- (delete-region (point-min) (line-beginning-position 2))
- (mapc (lambda (w) (apply #'set-window-start w))
- window-starts)
- (goto-char (1+ (cdr (assoc 'Cursor json))))))
+ (let (status stderr operations)
+ (setq status
+ (call-process-region
+ (point-min) (point-max) clang-format-executable
+ nil `(,temp-buffer ,temp-file) nil
+
+ "-output-replacements-xml"
+ "-assume-filename" (or (buffer-file-name) "")
+ "-style" style
+ "-offset" (number-to-string (1- start))
+ "-length" (number-to-string (- end start))
+ "-cursor" (number-to-string (1- (point)))))
+ (setq stderr
+ (with-temp-buffer
+ (insert-file-contents temp-file)
+ (when (> (point-max) (point-min))
+ (insert ": "))
+ (buffer-substring-no-properties
+ (point-min) (line-end-position))))
+
+ (cond
+ ((stringp status)
+ (error "(clang-format killed by signal %s%s)" status stderr))
+ ((not (equal 0 status))
+ (error "(clang-format failed with code %d%s)" status stderr))
+ (t (message "(clang-format succeeded%s)" stderr)))
+
+ (with-current-buffer temp-buffer
+ (setq operations (clang-format--extract (car (xml-parse-region)))))
+
+ (let ((replacements (car operations))
+ (cursor (cdr operations)))
+ (save-excursion
+ (mapc (lambda (rpl)
+ (apply #'clang-format--replace rpl))
+ replacements))
+ (when cursor
+ (goto-char cursor))))
+ (delete-file temp-file)
+ (when (buffer-name temp-buffer) (kill-buffer temp-buffer)))))
;;;###autoload
(defun clang-format-buffer (&optional style)