1;;; clang-rename.el --- Renames every occurrence of a symbol found at <offset>.  -*- lexical-binding: t; -*-
2
3;; Keywords: tools, c
4
5;;; Commentary:
6
7;; To install clang-rename.el make sure the directory of this file is in your
8;; `load-path' and add
9;;
10;;   (require 'clang-rename)
11;;
12;; to your .emacs configuration.
13
14;;; Code:
15
16(defgroup clang-rename nil
17  "Integration with clang-rename"
18  :group 'c)
19
20(defcustom clang-rename-binary "clang-rename"
21  "Path to clang-rename executable."
22  :type '(file :must-match t)
23  :group 'clang-rename)
24
25;;;###autoload
26(defun clang-rename (new-name)
27  "Rename all instances of the symbol at point to NEW-NAME using clang-rename."
28  (interactive "sEnter a new name: ")
29  (save-some-buffers :all)
30  ;; clang-rename should not be combined with other operations when undoing.
31  (undo-boundary)
32  (let ((output-buffer (get-buffer-create "*clang-rename*")))
33    (with-current-buffer output-buffer (erase-buffer))
34    (let ((exit-code (call-process
35                      clang-rename-binary nil output-buffer nil
36                      (format "-offset=%d"
37                              ;; clang-rename wants file (byte) offsets, not
38                              ;; buffer (character) positions.
39                              (clang-rename--bufferpos-to-filepos
40                               ;; Emacs treats one character after a symbol as
41                               ;; part of the symbol, but clang-rename doesn’t.
42                               ;; Use the beginning of the current symbol, if
43                               ;; available, to resolve the inconsistency.
44                               (or (car (bounds-of-thing-at-point 'symbol))
45                                   (point))
46                               'exact))
47                      (format "-new-name=%s" new-name)
48                      "-i" (buffer-file-name))))
49      (if (and (integerp exit-code) (zerop exit-code))
50          ;; Success; revert current buffer so it gets the modifications.
51          (progn
52            (kill-buffer output-buffer)
53            (revert-buffer :ignore-auto :noconfirm :preserve-modes))
54        ;; Failure; append exit code to output buffer and display it.
55        (let ((message (clang-rename--format-message
56                        "clang-rename failed with %s %s"
57                        (if (integerp exit-code) "exit status" "signal")
58                        exit-code)))
59          (with-current-buffer output-buffer
60            (insert ?\n message ?\n))
61          (message "%s" message)
62          (display-buffer output-buffer))))))
63
64(defalias 'clang-rename--bufferpos-to-filepos
65  (if (fboundp 'bufferpos-to-filepos)
66      'bufferpos-to-filepos
67    ;; Emacs 24 doesn’t have ‘bufferpos-to-filepos’, simulate it using
68    ;; ‘position-bytes’.
69    (lambda (position &optional _quality _coding-system)
70      (1- (position-bytes position)))))
71
72;; ‘format-message’ is new in Emacs 25.1.  Provide a fallback for older
73;; versions.
74(defalias 'clang-rename--format-message
75  (if (fboundp 'format-message) 'format-message 'format))
76
77(provide 'clang-rename)
78
79;;; clang-rename.el ends here
80