blob: 690fcda4c454249d8aae927d180f0a32dec36898 [file] [log] [blame]
Douglas Gregora584fb32009-10-09 22:17:40 +00001;;; Clang Code-Completion minor mode, for use with C/Objective-C/C++.
2
3;;; Commentary:
4
5;; This minor mode uses Clang's command line interface for code
6;; completion to provide code completion results for C, Objective-C,
7;; and C++ source files. When enabled, Clang will provide
8;; code-completion results in a secondary buffer based on the code
9;; being typed. For example, after typing "struct " (triggered via the
10;; space), Clang will provide the names of all structs visible from
11;; the current scope. After typing "p->" (triggered via the ">"),
12;; Clang will provide the names of all of the members of whatever
13;; class/struct/union "p" points to. Note that this minor mode isn't
14;; meant for serious use: it is meant to help experiment with code
15;; completion based on Clang. It needs your help to make it better!
16;;
17;; To use the Clang code completion mode, first make sure that the
18;; "clang-cc" variable below refers to the "clang-cc" executable,
19;; which is typically installed in libexec/. Then, place
20;; clang-completion-mode.el somewhere in your Emacs load path. You can
21;; add a new load path to Emacs by adding some like the following to
22;; your .emacs:
23;;
24;; (setq load-path (cons "~/.emacs.d" load-path))
25;;
26;; Then, use
27;;
28;; M-x load-library
29;;
30;; to load the library in your Emacs session or add the following to
31;; your .emacs to always load this mode (not recommended):
32;;
33;; (load-library "clang-completion-mode")
34;;
35;; Finally, to try Clang-based code completion in a particular buffer,
36;; use M-x clang-completion-mode. When "Clang-CC" shows up in the mode
37;; line, Clang's code-completion is enabled.
38;;
39;; Clang's code completion is based on parsing the complete source
40;; file up to the point where the cursor is located. Therefore, Clang
41;; needs all of the various compilation flags (include paths, dialect
42;; options, etc.) to provide code-completion results. Currently, these
43;; need to be placed into the clang-cc-flags variable in a format
44;; acceptable to clang-cc. This is a hack: patches are welcome to
45;; improve the interface between this Emacs mode and Clang!
46;;
47
48;;; Code:
49;;; The clang-cc executable
50(defcustom clang-cc "clang-cc"
51 "The location of the clang-cc executable of the Clang compiler.
52This executable is typically installed into the libexec subdirectory."
53 :type 'file
54 :group 'clang-completion-mode)
55
56;;; Extra compilation flags to pass to clang-cc.
57(defcustom clang-cc-flags ""
58 "Extra flags to pass to the Clang executable.
59This variable will typically contain include paths, e.g., -I~/MyProject."
60 :type 'string
61 :group 'clang-completion-mode)
62
63;;; The prefix header to use with Clang code completion.
64(setq clang-completion-prefix-header "")
65
66;;; The substring we will use to filter completion results
67(setq clang-completion-substring "")
68
69;;; The current completion buffer
70(setq clang-completion-buffer nil)
71
72(setq clang-cc-result-string "")
73
74;;; Compute the current line in the buffer
75(defun current-line ()
76 "Return the vertical position of point..."
77 (+ (count-lines (point-min) (point))
78 (if (= (current-column) 0) 1 0)
79 -1))
80
81;;; Set the Clang prefix header
82(defun clang-prefix-header ()
83 (interactive)
84 (setq clang-completion-prefix-header
85 (read-string "Clang prefix header> " "" clang-completion-prefix-header
86 "")))
87
88;; Process "filter" that keeps track of the code-completion results
89;; produced. We store all of the results in a string, then the
90;; sentinel processes the entire string at once.
91(defun clang-completion-stash-filter (proc string)
92 (setq clang-cc-result-string (concat clang-cc-result-string string)))
93
94;; Filter the given list based on a predicate.
95(defun filter (condp lst)
96 (delq nil
97 (mapcar (lambda (x) (and (funcall condp x) x)) lst)))
98
99;; Determine whether
100(defun is-completion-line (line)
101 (or (string-match "OVERLOAD:" line)
102 (string-match (concat "COMPLETION: " clang-completion-substring) line)))
103
104(defun clang-completion-display (buffer)
105 (let* ((all-lines (split-string clang-cc-result-string "\n"))
106 (completion-lines (filter 'is-completion-line all-lines)))
107 (if (consp completion-lines)
108 (progn
109 ;; Erase the process buffer
110 (let ((cur (current-buffer)))
111 (set-buffer buffer)
112 (goto-char (point-min))
113 (erase-buffer)
114 (set-buffer cur))
115
116 ;; Display the process buffer
117 (display-buffer buffer)
118
119 ;; Insert the code-completion string into the process buffer.
120 (with-current-buffer buffer
121 (insert (mapconcat 'identity completion-lines "\n")))
122 ))))
123
124;; Process "sentinal" that, on successful code completion, replaces the
125;; contents of the code-completion buffer with the new code-completion results
126;; and ensures that the buffer is visible.
127(defun clang-completion-sentinel (proc event)
128 (let* ((all-lines (split-string clang-cc-result-string "\n"))
129 (completion-lines (filter 'is-completion-line all-lines)))
130 (if (consp completion-lines)
131 (progn
132 ;; Erase the process buffer
133 (let ((cur (current-buffer)))
134 (set-buffer (process-buffer proc))
135 (goto-char (point-min))
136 (erase-buffer)
137 (set-buffer cur))
138
139 ;; Display the process buffer
140 (display-buffer (process-buffer proc))
141
142 ;; Insert the code-completion string into the process buffer.
143 (with-current-buffer (process-buffer proc)
144 (insert (mapconcat 'identity completion-lines "\n")))
145 ))))
146
147(defun clang-complete ()
148 (let ((ccstring (concat "-code-completion-at="
149 (buffer-file-name)
150 ":"
151 (number-to-string (+ 1 (current-line)))
152 ":"
153 (number-to-string (+ 1 (current-column)))))
154 (cc-buffer-name (concat "*Clang Completion for " (buffer-name) "*")))
155 ;; Start the code-completion process
156 (if (buffer-file-name)
157 (progn
158 ;; If there is already a code-completion process, kill it first.
159 (let ((cc-proc (get-process "Clang Code-Completion")))
160 (if cc-proc
161 (delete-process cc-proc)))
162
163 (setq clang-completion-substring "")
164 (setq clang-cc-result-string "")
165 (setq clang-completion-buffer cc-buffer-name)
166
167 (let ((cc-proc
168 (if (equal clang-completion-prefix-header "")
169 (start-process "Clang Code-Completion" cc-buffer-name
170 clang-cc "-fsyntax-only" ccstring
171 (buffer-file-name))
172 (start-process "Clang Code-Completion" cc-buffer-name
173 clang-cc "-fsyntax-only" ccstring
174 "-include-pch"
175 (concat clang-completion-prefix-header ".pch")
176 (buffer-file-name)))))
177 (set-process-filter cc-proc 'clang-completion-stash-filter)
178 (set-process-sentinel cc-proc 'clang-completion-sentinel)
179 )))))
180
181;; Code-completion when one of the trigger characters is typed into
182;; the buffer, e.g., '(', ',' or '.'.
183(defun clang-complete-self-insert (arg)
184 (interactive "p")
185 (self-insert-command arg)
186 (save-buffer)
187 (clang-complete))
188
189;; When the user has typed a character that requires the filter to be
190;; updated, do so (and update the display of results).
191(defun clang-update-filter ()
192 (setq clang-completion-substring (thing-at-point 'symbol))
193 (if (get-process "Clang Code-Completion")
194 ()
195 (clang-completion-display clang-completion-buffer)
196 ))
197
198;; Invoked when the user types an alphanumeric character or "_" to
199;; update the filter for the currently-active code completion.
200(defun clang-filter-self-insert (arg)
201 (interactive "p")
202 (self-insert-command arg)
203 (clang-update-filter)
204 )
205
206;; Invoked when the user types the backspace key to update the filter
207;; for the currently-active code completion.
208(defun clang-backspace ()
209 (interactive)
210 (delete-backward-char 1)
211 (clang-update-filter))
212
213;; Invoked when the user types the delete key to update the filter
214;; for the currently-active code completion.
215(defun clang-delete ()
216 (interactive)
217 (delete-backward-char 1)
218 (clang-update-filter))
219
220;; Set up the keymap for the Clang minor mode.
221(defvar clang-completion-mode-map nil
222 "Keymap for Clang Completion Mode.")
223
224(if (null clang-completion-mode-map)
225 (fset 'clang-completion-mode-map
226 (setq clang-completion-mode-map (make-sparse-keymap))))
227
228(if (not (assq 'clang-completion-mode minor-mode-map-alist))
229 (setq minor-mode-map-alist
230 (cons (cons 'clang-completion-mode clang-completion-mode-map)
231 minor-mode-map-alist)))
232
233;; Punctuation characters trigger code completion.
234(dolist (char '("(" "," "." ">" ":" "=" ")" " "))
235 (define-key clang-completion-mode-map char 'clang-complete-self-insert))
236
237;; Alphanumeric characters (and "_") filter the results of the
238;; currently-active code completion.
239(dolist (char '("A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O"
240 "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
241 "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o"
242 "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
243 "_" "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"))
244 (define-key clang-completion-mode-map char 'clang-filter-self-insert))
245
246;; Delete and backspace filter the results of the currently-active
247;; code completion.
248(define-key clang-completion-mode-map [(backspace)] 'clang-backspace)
249(define-key clang-completion-mode-map [(delete)] 'clang-delete)
250
251;; Set up the Clang minor mode.
252(define-minor-mode clang-completion-mode
253 "Clang code-completion mode"
254 nil
255 " Clang-CC"
256 clang-completion-mode-map)
257