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)
 | 
						|
 |