250 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
			
		
		
	
	
			250 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
| ;;; 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")
 | |
| ;;
 | |
| ;; Once you have done this, you can set various parameters with
 | |
| ;;
 | |
| ;;   M-x customize-group RET clang-completion-mode RET
 | |
| ;;
 | |
| ;; Finally, to try Clang-based code completion in a particular buffer,
 | |
| ;; use M-x clang-completion-mode. When "Clang" 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 FIXME: explain better
 | |
| (defun is-completion-line (line)
 | |
|   (or (string-match "OVERLOAD:" line)
 | |
|       (string-match (concat "COMPLETION: " clang-completion-substring) line)))
 | |
| 
 | |
| 
 | |
| ;; re-process the completions when further input narrows the field
 | |
| (defun clang-completion-display (buffer)
 | |
|   (fill-buffer buffer))
 | |
| 
 | |
| (defun fill-buffer (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 "sentinel" 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)
 | |
|   (fill-buffer (process-buffer proc)))
 | |
| 
 | |
| (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)
 | |
| 
 |