Home | History | Annotate | Download | only in utils
      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
     18 ;; "clang" variable below refers to the "clang" 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 ;; Once you have done this, you can set various parameters with
     36 ;;
     37 ;;   M-x customize-group RET clang-completion-mode RET
     38 ;;
     39 ;; Finally, to try Clang-based code completion in a particular buffer,
     40 ;; use M-x clang-completion-mode. When "Clang" shows up in the mode
     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
     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
     49 ;; improve the interface between this Emacs mode and Clang!
     50 ;;
     51 
     52 ;;; Code:
     53 ;;; The clang executable
     54 (defcustom clang "clang"
     55   "The location of the Clang compiler executable"
     56   :type 'file
     57   :group 'clang-completion-mode)
     58 
     59 ;;; Extra compilation flags to pass to clang.
     60 (defcustom clang-flags nil
     61   "Extra flags to pass to the Clang executable.
     62 This variable will typically contain include paths, e.g., -I~/MyProject."
     63   :type '(repeat (string :tag "Argument" ""))
     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 
     75 (setq clang-result-string "")
     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)
     95   (setq clang-result-string (concat clang-result-string string)))
     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 
    102 ;; Determine whether FIXME: explain better
    103 (defun is-completion-line (line)
    104   (or (string-match "OVERLOAD:" line)
    105       (string-match (concat "COMPLETION: " clang-completion-substring) line)))
    106 
    107 
    108 ;; re-process the completions when further input narrows the field
    109 (defun clang-completion-display (buffer)
    110   (fill-buffer buffer))
    111 
    112 (defun fill-buffer (buffer)
    113   (let* ((all-lines (split-string clang-result-string "\n"))
    114          (completion-lines (filter 'is-completion-line all-lines)))
    115     (if (consp completion-lines)
    116         (progn
    117          ;; Erase the process buffer.
    118          (let ((cur (current-buffer)))
    119            (set-buffer buffer)
    120            (goto-char (point-min))
    121            (erase-buffer)
    122            (set-buffer cur))
    123 
    124          ;; Display the process buffer.
    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 
    132 ;; Process "sentinel" that, on successful code completion, replaces the
    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)
    136   (fill-buffer (process-buffer proc)))
    137 
    138 (defun clang-complete ()
    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) "*")))
    154     ;; Start the code-completion process.
    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 "")
    163           (setq clang-result-string "")
    164           (setq clang-completion-buffer cc-buffer-name)
    165 
    166           (let ((cc-proc (apply 'start-process
    167                                 (append (list "Clang Code-Completion" cc-buffer-name)
    168                                         cc-command))))
    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
    247   " Clang"
    248   clang-completion-mode-map)
    249 
    250