[cfe-commits] r83681 - /cfe/trunk/utils/clang-completion-mode.el

Douglas Gregor dgregor at apple.com
Fri Oct 9 15:17:40 PDT 2009


Author: dgregor
Date: Fri Oct  9 17:17:40 2009
New Revision: 83681

URL: http://llvm.org/viewvc/llvm-project?rev=83681&view=rev
Log:
Experimental, ultra-hacking Emacs minor mode for Clang-based code completion.

Added:
    cfe/trunk/utils/clang-completion-mode.el

Added: cfe/trunk/utils/clang-completion-mode.el
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/clang-completion-mode.el?rev=83681&view=auto

==============================================================================
--- cfe/trunk/utils/clang-completion-mode.el (added)
+++ cfe/trunk/utils/clang-completion-mode.el Fri Oct  9 17:17:40 2009
@@ -0,0 +1,257 @@
+;;; 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-cc" variable below refers to the "clang-cc" 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-cc-flags variable in a format
+;; acceptable to clang-cc. This is a hack: patches are welcome to
+;; improve the interface between this Emacs mode and Clang! 
+;;
+
+;;; Code:
+;;; The clang-cc executable
+(defcustom clang-cc "clang-cc"
+  "The location of the clang-cc executable of the Clang compiler.
+This executable is typically installed into the libexec subdirectory."
+  :type 'file
+  :group 'clang-completion-mode)
+
+;;; Extra compilation flags to pass to clang-cc.
+(defcustom clang-cc-flags ""
+  "Extra flags to pass to the Clang executable.
+This variable will typically contain include paths, e.g., -I~/MyProject."
+  :type 'string
+  :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-cc-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-cc-result-string (concat clang-cc-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-cc-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-cc-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 ((ccstring (concat "-code-completion-at="
+                          (buffer-file-name)
+                          ":"
+                          (number-to-string (+ 1 (current-line)))
+                          ":"
+                          (number-to-string (+ 1 (current-column)))))
+        (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-cc-result-string "")
+          (setq clang-completion-buffer cc-buffer-name)
+            
+          (let ((cc-proc
+                 (if (equal clang-completion-prefix-header "")
+                     (start-process "Clang Code-Completion" cc-buffer-name
+                                    clang-cc "-fsyntax-only" ccstring 
+                                    (buffer-file-name))
+                     (start-process "Clang Code-Completion" cc-buffer-name
+                                    clang-cc "-fsyntax-only" ccstring 
+                                    "-include-pch" 
+                                    (concat clang-completion-prefix-header ".pch")
+                                    (buffer-file-name)))))
+            (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-CC"
+  clang-completion-mode-map)
+





More information about the cfe-commits mailing list