| ;;; Clang Code-Completion minor mode, for use with C/Objective-C/C++. |
| |
| ;;; Commentary: |
| |
| ;; This minor mode uses Clang's command line interface for code |
| ;; completion to provide code completion results for C, Objective-C, |
| ;; and C++ source files. When enabled, Clang will provide |
| ;; code-completion results in a secondary buffer based on the code |
| ;; being typed. For example, after typing "struct " (triggered via the |
| ;; space), Clang will provide the names of all structs visible from |
| ;; the current scope. After typing "p->" (triggered via the ">"), |
| ;; Clang will provide the names of all of the members of whatever |
| ;; class/struct/union "p" points to. Note that this minor mode isn't |
| ;; meant for serious use: it is meant to help experiment with code |
| ;; completion based on Clang. It needs your help to make it better! |
| ;; |
| ;; To use the Clang code completion mode, first make sure that the |
| ;; "clang" variable below refers to the "clang" executable, |
| ;; which is typically installed in libexec/. Then, place |
| ;; clang-completion-mode.el somewhere in your Emacs load path. You can |
| ;; add a new load path to Emacs by adding some like the following to |
| ;; your .emacs: |
| ;; |
| ;; (setq load-path (cons "~/.emacs.d" load-path)) |
| ;; |
| ;; Then, use |
| ;; |
| ;; M-x load-library |
| ;; |
| ;; to load the library in your Emacs session or add the following to |
| ;; your .emacs to always load this mode (not recommended): |
| ;; |
| ;; (load-library "clang-completion-mode") |
| ;; |
| ;; Finally, to try Clang-based code completion in a particular buffer, |
| ;; use M-x clang-completion-mode. When "Clang-CC" shows up in the mode |
| ;; line, Clang's code-completion is enabled. |
| ;; |
| ;; Clang's code completion is based on parsing the complete source |
| ;; file up to the point where the cursor is located. Therefore, Clang |
| ;; needs all of the various compilation flags (include paths, dialect |
| ;; options, etc.) to provide code-completion results. Currently, these |
| ;; need to be placed into the clang-flags variable in a format |
| ;; acceptable to clang. This is a hack: patches are welcome to |
| ;; improve the interface between this Emacs mode and Clang! |
| ;; |
| |
| ;;; Code: |
| ;;; The clang executable |
| (defcustom clang "clang" |
| "The location of the Clang compiler executable" |
| :type 'file |
| :group 'clang-completion-mode) |
| |
| ;;; Extra compilation flags to pass to clang. |
| (defcustom clang-flags nil |
| "Extra flags to pass to the Clang executable. |
| This variable will typically contain include paths, e.g., -I~/MyProject." |
| :type '(repeat (string :tag "Argument" "")) |
| :group 'clang-completion-mode) |
| |
| ;;; The prefix header to use with Clang code completion. |
| (setq clang-completion-prefix-header "") |
| |
| ;;; The substring we will use to filter completion results |
| (setq clang-completion-substring "") |
| |
| ;;; The current completion buffer |
| (setq clang-completion-buffer nil) |
| |
| (setq clang-result-string "") |
| |
| ;;; Compute the current line in the buffer |
| (defun current-line () |
| "Return the vertical position of point..." |
| (+ (count-lines (point-min) (point)) |
| (if (= (current-column) 0) 1 0) |
| -1)) |
| |
| ;;; Set the Clang prefix header |
| (defun clang-prefix-header () |
| (interactive) |
| (setq clang-completion-prefix-header |
| (read-string "Clang prefix header> " "" clang-completion-prefix-header |
| ""))) |
| |
| ;; Process "filter" that keeps track of the code-completion results |
| ;; produced. We store all of the results in a string, then the |
| ;; sentinel processes the entire string at once. |
| (defun clang-completion-stash-filter (proc string) |
| (setq clang-result-string (concat clang-result-string string))) |
| |
| ;; Filter the given list based on a predicate. |
| (defun filter (condp lst) |
| (delq nil |
| (mapcar (lambda (x) (and (funcall condp x) x)) lst))) |
| |
| ;; Determine whether |
| (defun is-completion-line (line) |
| (or (string-match "OVERLOAD:" line) |
| (string-match (concat "COMPLETION: " clang-completion-substring) line))) |
| |
| (defun clang-completion-display (buffer) |
| (let* ((all-lines (split-string clang-result-string "\n")) |
| (completion-lines (filter 'is-completion-line all-lines))) |
| (if (consp completion-lines) |
| (progn |
| ;; Erase the process buffer |
| (let ((cur (current-buffer))) |
| (set-buffer buffer) |
| (goto-char (point-min)) |
| (erase-buffer) |
| (set-buffer cur)) |
| |
| ;; Display the process buffer |
| (display-buffer buffer) |
| |
| ;; Insert the code-completion string into the process buffer. |
| (with-current-buffer buffer |
| (insert (mapconcat 'identity completion-lines "\n"))) |
| )))) |
| |
| ;; Process "sentinal" that, on successful code completion, replaces the |
| ;; contents of the code-completion buffer with the new code-completion results |
| ;; and ensures that the buffer is visible. |
| (defun clang-completion-sentinel (proc event) |
| (let* ((all-lines (split-string clang-result-string "\n")) |
| (completion-lines (filter 'is-completion-line all-lines))) |
| (if (consp completion-lines) |
| (progn |
| ;; Erase the process buffer |
| (let ((cur (current-buffer))) |
| (set-buffer (process-buffer proc)) |
| (goto-char (point-min)) |
| (erase-buffer) |
| (set-buffer cur)) |
| |
| ;; Display the process buffer |
| (display-buffer (process-buffer proc)) |
| |
| ;; Insert the code-completion string into the process buffer. |
| (with-current-buffer (process-buffer proc) |
| (insert (mapconcat 'identity completion-lines "\n"))) |
| )))) |
| |
| (defun clang-complete () |
| (let* ((cc-point (concat (buffer-file-name) |
| ":" |
| (number-to-string (+ 1 (current-line))) |
| ":" |
| (number-to-string (+ 1 (current-column))))) |
| (cc-pch (if (equal clang-completion-prefix-header "") nil |
| (list "-include-pch" |
| (concat clang-completion-prefix-header ".pch")))) |
| (cc-flags (if (listp clang-flags) clang-flags nil)) |
| (cc-command (append `(,clang "-cc1" "-fsyntax-only") |
| cc-flags |
| cc-pch |
| `("-code-completion-at" ,cc-point) |
| (list (buffer-file-name)))) |
| (cc-buffer-name (concat "*Clang Completion for " (buffer-name) "*"))) |
| ;; Start the code-completion process |
| (if (buffer-file-name) |
| (progn |
| ;; If there is already a code-completion process, kill it first. |
| (let ((cc-proc (get-process "Clang Code-Completion"))) |
| (if cc-proc |
| (delete-process cc-proc))) |
| |
| (setq clang-completion-substring "") |
| (setq clang-result-string "") |
| (setq clang-completion-buffer cc-buffer-name) |
| |
| (let ((cc-proc (apply 'start-process |
| (append (list "Clang Code-Completion" cc-buffer-name) |
| cc-command)))) |
| (set-process-filter cc-proc 'clang-completion-stash-filter) |
| (set-process-sentinel cc-proc 'clang-completion-sentinel) |
| ))))) |
| |
| ;; Code-completion when one of the trigger characters is typed into |
| ;; the buffer, e.g., '(', ',' or '.'. |
| (defun clang-complete-self-insert (arg) |
| (interactive "p") |
| (self-insert-command arg) |
| (save-buffer) |
| (clang-complete)) |
| |
| ;; When the user has typed a character that requires the filter to be |
| ;; updated, do so (and update the display of results). |
| (defun clang-update-filter () |
| (setq clang-completion-substring (thing-at-point 'symbol)) |
| (if (get-process "Clang Code-Completion") |
| () |
| (clang-completion-display clang-completion-buffer) |
| )) |
| |
| ;; Invoked when the user types an alphanumeric character or "_" to |
| ;; update the filter for the currently-active code completion. |
| (defun clang-filter-self-insert (arg) |
| (interactive "p") |
| (self-insert-command arg) |
| (clang-update-filter) |
| ) |
| |
| ;; Invoked when the user types the backspace key to update the filter |
| ;; for the currently-active code completion. |
| (defun clang-backspace () |
| (interactive) |
| (delete-backward-char 1) |
| (clang-update-filter)) |
| |
| ;; Invoked when the user types the delete key to update the filter |
| ;; for the currently-active code completion. |
| (defun clang-delete () |
| (interactive) |
| (delete-backward-char 1) |
| (clang-update-filter)) |
| |
| ;; Set up the keymap for the Clang minor mode. |
| (defvar clang-completion-mode-map nil |
| "Keymap for Clang Completion Mode.") |
| |
| (if (null clang-completion-mode-map) |
| (fset 'clang-completion-mode-map |
| (setq clang-completion-mode-map (make-sparse-keymap)))) |
| |
| (if (not (assq 'clang-completion-mode minor-mode-map-alist)) |
| (setq minor-mode-map-alist |
| (cons (cons 'clang-completion-mode clang-completion-mode-map) |
| minor-mode-map-alist))) |
| |
| ;; Punctuation characters trigger code completion. |
| (dolist (char '("(" "," "." ">" ":" "=" ")" " ")) |
| (define-key clang-completion-mode-map char 'clang-complete-self-insert)) |
| |
| ;; Alphanumeric characters (and "_") filter the results of the |
| ;; currently-active code completion. |
| (dolist (char '("A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" |
| "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" |
| "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" |
| "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z" |
| "_" "0" "1" "2" "3" "4" "5" "6" "7" "8" "9")) |
| (define-key clang-completion-mode-map char 'clang-filter-self-insert)) |
| |
| ;; Delete and backspace filter the results of the currently-active |
| ;; code completion. |
| (define-key clang-completion-mode-map [(backspace)] 'clang-backspace) |
| (define-key clang-completion-mode-map [(delete)] 'clang-delete) |
| |
| ;; Set up the Clang minor mode. |
| (define-minor-mode clang-completion-mode |
| "Clang code-completion mode" |
| nil |
| " Clang" |
| clang-completion-mode-map) |
| |