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