xref: /aosp_15_r20/external/clang/utils/clang-completion-mode.el (revision 67e74705e28f6214e480b399dd47ea732279e315)
1*67e74705SXin Li;;; Clang Code-Completion minor mode, for use with C/Objective-C/C++.
2*67e74705SXin Li
3*67e74705SXin Li;;; Commentary:
4*67e74705SXin Li
5*67e74705SXin Li;; This minor mode uses Clang's command line interface for code
6*67e74705SXin Li;; completion to provide code completion results for C, Objective-C,
7*67e74705SXin Li;; and C++ source files. When enabled, Clang will provide
8*67e74705SXin Li;; code-completion results in a secondary buffer based on the code
9*67e74705SXin Li;; being typed. For example, after typing "struct " (triggered via the
10*67e74705SXin Li;; space), Clang will provide the names of all structs visible from
11*67e74705SXin Li;; the current scope. After typing "p->" (triggered via the ">"),
12*67e74705SXin Li;; Clang will provide the names of all of the members of whatever
13*67e74705SXin Li;; class/struct/union "p" points to. Note that this minor mode isn't
14*67e74705SXin Li;; meant for serious use: it is meant to help experiment with code
15*67e74705SXin Li;; completion based on Clang. It needs your help to make it better!
16*67e74705SXin Li;;
17*67e74705SXin Li;; To use the Clang code completion mode, first make sure that the
18*67e74705SXin Li;; "clang" variable below refers to the "clang" executable,
19*67e74705SXin Li;; which is typically installed in libexec/. Then, place
20*67e74705SXin Li;; clang-completion-mode.el somewhere in your Emacs load path. You can
21*67e74705SXin Li;; add a new load path to Emacs by adding some like the following to
22*67e74705SXin Li;; your .emacs:
23*67e74705SXin Li;;
24*67e74705SXin Li;;   (setq load-path (cons "~/.emacs.d" load-path))
25*67e74705SXin Li;;
26*67e74705SXin Li;; Then, use
27*67e74705SXin Li;;
28*67e74705SXin Li;;   M-x load-library
29*67e74705SXin Li;;
30*67e74705SXin Li;; to load the library in your Emacs session or add the following to
31*67e74705SXin Li;; your .emacs to always load this mode (not recommended):
32*67e74705SXin Li;;
33*67e74705SXin Li;;   (load-library "clang-completion-mode")
34*67e74705SXin Li;;
35*67e74705SXin Li;; Once you have done this, you can set various parameters with
36*67e74705SXin Li;;
37*67e74705SXin Li;;   M-x customize-group RET clang-completion-mode RET
38*67e74705SXin Li;;
39*67e74705SXin Li;; Finally, to try Clang-based code completion in a particular buffer,
40*67e74705SXin Li;; use M-x clang-completion-mode. When "Clang" shows up in the mode
41*67e74705SXin Li;; line, Clang's code-completion is enabled.
42*67e74705SXin Li;;
43*67e74705SXin Li;; Clang's code completion is based on parsing the complete source
44*67e74705SXin Li;; file up to the point where the cursor is located. Therefore, Clang
45*67e74705SXin Li;; needs all of the various compilation flags (include paths, dialect
46*67e74705SXin Li;; options, etc.) to provide code-completion results. Currently, these
47*67e74705SXin Li;; need to be placed into the clang-flags variable in a format
48*67e74705SXin Li;; acceptable to clang. This is a hack: patches are welcome to
49*67e74705SXin Li;; improve the interface between this Emacs mode and Clang!
50*67e74705SXin Li;;
51*67e74705SXin Li
52*67e74705SXin Li;;; Code:
53*67e74705SXin Li;;; The clang executable
54*67e74705SXin Li(defcustom clang "clang"
55*67e74705SXin Li  "The location of the Clang compiler executable"
56*67e74705SXin Li  :type 'file
57*67e74705SXin Li  :group 'clang-completion-mode)
58*67e74705SXin Li
59*67e74705SXin Li;;; Extra compilation flags to pass to clang.
60*67e74705SXin Li(defcustom clang-flags nil
61*67e74705SXin Li  "Extra flags to pass to the Clang executable.
62*67e74705SXin LiThis variable will typically contain include paths, e.g., -I~/MyProject."
63*67e74705SXin Li  :type '(repeat (string :tag "Argument" ""))
64*67e74705SXin Li  :group 'clang-completion-mode)
65*67e74705SXin Li
66*67e74705SXin Li;;; The prefix header to use with Clang code completion.
67*67e74705SXin Li(setq clang-completion-prefix-header "")
68*67e74705SXin Li
69*67e74705SXin Li;;; The substring we will use to filter completion results
70*67e74705SXin Li(setq clang-completion-substring "")
71*67e74705SXin Li
72*67e74705SXin Li;;; The current completion buffer
73*67e74705SXin Li(setq clang-completion-buffer nil)
74*67e74705SXin Li
75*67e74705SXin Li(setq clang-result-string "")
76*67e74705SXin Li
77*67e74705SXin Li;;; Compute the current line in the buffer
78*67e74705SXin Li(defun current-line ()
79*67e74705SXin Li  "Return the vertical position of point..."
80*67e74705SXin Li  (+ (count-lines (point-min) (point))
81*67e74705SXin Li     (if (= (current-column) 0) 1 0)
82*67e74705SXin Li     -1))
83*67e74705SXin Li
84*67e74705SXin Li;;; Set the Clang prefix header
85*67e74705SXin Li(defun clang-prefix-header ()
86*67e74705SXin Li  (interactive)
87*67e74705SXin Li  (setq clang-completion-prefix-header
88*67e74705SXin Li        (read-string "Clang prefix header> " "" clang-completion-prefix-header
89*67e74705SXin Li                     "")))
90*67e74705SXin Li
91*67e74705SXin Li;; Process "filter" that keeps track of the code-completion results
92*67e74705SXin Li;; produced. We store all of the results in a string, then the
93*67e74705SXin Li;; sentinel processes the entire string at once.
94*67e74705SXin Li(defun clang-completion-stash-filter (proc string)
95*67e74705SXin Li  (setq clang-result-string (concat clang-result-string string)))
96*67e74705SXin Li
97*67e74705SXin Li;; Filter the given list based on a predicate.
98*67e74705SXin Li(defun filter (condp lst)
99*67e74705SXin Li    (delq nil
100*67e74705SXin Li          (mapcar (lambda (x) (and (funcall condp x) x)) lst)))
101*67e74705SXin Li
102*67e74705SXin Li;; Determine whether FIXME: explain better
103*67e74705SXin Li(defun is-completion-line (line)
104*67e74705SXin Li  (or (string-match "OVERLOAD:" line)
105*67e74705SXin Li      (string-match (concat "COMPLETION: " clang-completion-substring) line)))
106*67e74705SXin Li
107*67e74705SXin Li
108*67e74705SXin Li;; re-process the completions when further input narrows the field
109*67e74705SXin Li(defun clang-completion-display (buffer)
110*67e74705SXin Li  (fill-buffer buffer))
111*67e74705SXin Li
112*67e74705SXin Li(defun fill-buffer (buffer)
113*67e74705SXin Li  (let* ((all-lines (split-string clang-result-string "\n"))
114*67e74705SXin Li         (completion-lines (filter 'is-completion-line all-lines)))
115*67e74705SXin Li    (if (consp completion-lines)
116*67e74705SXin Li        (progn
117*67e74705SXin Li         ;; Erase the process buffer.
118*67e74705SXin Li         (let ((cur (current-buffer)))
119*67e74705SXin Li           (set-buffer buffer)
120*67e74705SXin Li           (goto-char (point-min))
121*67e74705SXin Li           (erase-buffer)
122*67e74705SXin Li           (set-buffer cur))
123*67e74705SXin Li
124*67e74705SXin Li         ;; Display the process buffer.
125*67e74705SXin Li         (display-buffer buffer)
126*67e74705SXin Li
127*67e74705SXin Li         ;; Insert the code-completion string into the process buffer.
128*67e74705SXin Li         (with-current-buffer buffer
129*67e74705SXin Li           (insert (mapconcat 'identity completion-lines "\n")))
130*67e74705SXin Li         ))))
131*67e74705SXin Li
132*67e74705SXin Li;; Process "sentinel" that, on successful code completion, replaces the
133*67e74705SXin Li;; contents of the code-completion buffer with the new code-completion results
134*67e74705SXin Li;; and ensures that the buffer is visible.
135*67e74705SXin Li(defun clang-completion-sentinel (proc event)
136*67e74705SXin Li  (fill-buffer (process-buffer proc)))
137*67e74705SXin Li
138*67e74705SXin Li(defun clang-complete ()
139*67e74705SXin Li  (let* ((cc-point (concat (buffer-file-name)
140*67e74705SXin Li                           ":"
141*67e74705SXin Li                           (number-to-string (+ 1 (current-line)))
142*67e74705SXin Li                           ":"
143*67e74705SXin Li                           (number-to-string (+ 1 (current-column)))))
144*67e74705SXin Li         (cc-pch (if (equal clang-completion-prefix-header "") nil
145*67e74705SXin Li                   (list "-include-pch"
146*67e74705SXin Li                         (concat clang-completion-prefix-header ".pch"))))
147*67e74705SXin Li         (cc-flags (if (listp clang-flags) clang-flags nil))
148*67e74705SXin Li         (cc-command (append `(,clang "-cc1" "-fsyntax-only")
149*67e74705SXin Li                             cc-flags
150*67e74705SXin Li                             cc-pch
151*67e74705SXin Li                             `("-code-completion-at" ,cc-point)
152*67e74705SXin Li                             (list (buffer-file-name))))
153*67e74705SXin Li         (cc-buffer-name (concat "*Clang Completion for " (buffer-name) "*")))
154*67e74705SXin Li    ;; Start the code-completion process.
155*67e74705SXin Li    (if (buffer-file-name)
156*67e74705SXin Li        (progn
157*67e74705SXin Li          ;; If there is already a code-completion process, kill it first.
158*67e74705SXin Li          (let ((cc-proc (get-process "Clang Code-Completion")))
159*67e74705SXin Li            (if cc-proc
160*67e74705SXin Li                (delete-process cc-proc)))
161*67e74705SXin Li
162*67e74705SXin Li          (setq clang-completion-substring "")
163*67e74705SXin Li          (setq clang-result-string "")
164*67e74705SXin Li          (setq clang-completion-buffer cc-buffer-name)
165*67e74705SXin Li
166*67e74705SXin Li          (let ((cc-proc (apply 'start-process
167*67e74705SXin Li                                (append (list "Clang Code-Completion" cc-buffer-name)
168*67e74705SXin Li                                        cc-command))))
169*67e74705SXin Li            (set-process-filter cc-proc 'clang-completion-stash-filter)
170*67e74705SXin Li            (set-process-sentinel cc-proc 'clang-completion-sentinel)
171*67e74705SXin Li            )))))
172*67e74705SXin Li
173*67e74705SXin Li;; Code-completion when one of the trigger characters is typed into
174*67e74705SXin Li;; the buffer, e.g., '(', ',' or '.'.
175*67e74705SXin Li(defun clang-complete-self-insert (arg)
176*67e74705SXin Li  (interactive "p")
177*67e74705SXin Li  (self-insert-command arg)
178*67e74705SXin Li  (save-buffer)
179*67e74705SXin Li  (clang-complete))
180*67e74705SXin Li
181*67e74705SXin Li;; When the user has typed a character that requires the filter to be
182*67e74705SXin Li;; updated, do so (and update the display of results).
183*67e74705SXin Li(defun clang-update-filter ()
184*67e74705SXin Li  (setq clang-completion-substring (thing-at-point 'symbol))
185*67e74705SXin Li  (if (get-process "Clang Code-Completion")
186*67e74705SXin Li      ()
187*67e74705SXin Li    (clang-completion-display clang-completion-buffer)
188*67e74705SXin Li    ))
189*67e74705SXin Li
190*67e74705SXin Li;; Invoked when the user types an alphanumeric character or "_" to
191*67e74705SXin Li;; update the filter for the currently-active code completion.
192*67e74705SXin Li(defun clang-filter-self-insert (arg)
193*67e74705SXin Li  (interactive "p")
194*67e74705SXin Li  (self-insert-command arg)
195*67e74705SXin Li  (clang-update-filter)
196*67e74705SXin Li  )
197*67e74705SXin Li
198*67e74705SXin Li;; Invoked when the user types the backspace key to update the filter
199*67e74705SXin Li;; for the currently-active code completion.
200*67e74705SXin Li(defun clang-backspace ()
201*67e74705SXin Li  (interactive)
202*67e74705SXin Li  (delete-backward-char 1)
203*67e74705SXin Li  (clang-update-filter))
204*67e74705SXin Li
205*67e74705SXin Li;; Invoked when the user types the delete key to update the filter
206*67e74705SXin Li;; for the currently-active code completion.
207*67e74705SXin Li(defun clang-delete ()
208*67e74705SXin Li  (interactive)
209*67e74705SXin Li  (delete-backward-char 1)
210*67e74705SXin Li  (clang-update-filter))
211*67e74705SXin Li
212*67e74705SXin Li;; Set up the keymap for the Clang minor mode.
213*67e74705SXin Li(defvar clang-completion-mode-map nil
214*67e74705SXin Li  "Keymap for Clang Completion Mode.")
215*67e74705SXin Li
216*67e74705SXin Li(if (null clang-completion-mode-map)
217*67e74705SXin Li    (fset 'clang-completion-mode-map
218*67e74705SXin Li          (setq clang-completion-mode-map (make-sparse-keymap))))
219*67e74705SXin Li
220*67e74705SXin Li(if (not (assq 'clang-completion-mode minor-mode-map-alist))
221*67e74705SXin Li    (setq minor-mode-map-alist
222*67e74705SXin Li          (cons (cons 'clang-completion-mode clang-completion-mode-map)
223*67e74705SXin Li                minor-mode-map-alist)))
224*67e74705SXin Li
225*67e74705SXin Li;; Punctuation characters trigger code completion.
226*67e74705SXin Li(dolist (char '("(" "," "." ">" ":" "=" ")" " "))
227*67e74705SXin Li  (define-key clang-completion-mode-map char 'clang-complete-self-insert))
228*67e74705SXin Li
229*67e74705SXin Li;; Alphanumeric characters (and "_") filter the results of the
230*67e74705SXin Li;; currently-active code completion.
231*67e74705SXin Li(dolist (char '("A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O"
232*67e74705SXin Li                "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
233*67e74705SXin Li                "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o"
234*67e74705SXin Li                "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
235*67e74705SXin Li                "_" "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"))
236*67e74705SXin Li  (define-key clang-completion-mode-map char 'clang-filter-self-insert))
237*67e74705SXin Li
238*67e74705SXin Li;; Delete and backspace filter the results of the currently-active
239*67e74705SXin Li;; code completion.
240*67e74705SXin Li(define-key clang-completion-mode-map [(backspace)] 'clang-backspace)
241*67e74705SXin Li(define-key clang-completion-mode-map [(delete)] 'clang-delete)
242*67e74705SXin Li
243*67e74705SXin Li;; Set up the Clang minor mode.
244*67e74705SXin Li(define-minor-mode clang-completion-mode
245*67e74705SXin Li  "Clang code-completion mode"
246*67e74705SXin Li  nil
247*67e74705SXin Li  " Clang"
248*67e74705SXin Li  clang-completion-mode-map)
249*67e74705SXin Li
250