1 ;;; company-clang.el --- company-mode completion backend for Clang -*- lexical-binding: t -*-
3 ;; Copyright (C) 2009-2011, 2013-2023 Free Software Foundation, Inc.
5 ;; Author: Nikolaj Schumacher
7 ;; This file is part of GNU Emacs.
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
29 (require 'company-template)
32 (defgroup company-clang nil
33 "Completion backend for Clang."
36 (defcustom company-clang-executable
37 (executable-find "clang")
38 "Location of clang executable."
41 (defcustom company-clang-begin-after-member-access t
42 "When non-nil, start automatic completion after member access operators.
44 Automatic completion starts whenever the current symbol is preceded by
45 \".\", \"->\" or \"::\", ignoring `company-minimum-prefix-length'.
47 If `company-begin-commands' is a list, it should include `c-electric-lt-gt'
48 and `c-electric-colon', for automatic completion right after \">\" and
52 (defcustom company-clang-use-compile-flags-txt nil
53 "When non-nil, use flags from compile_flags.txt if present.
55 The lines from that files will be appended to `company-clang-arguments'.
57 And if such file is found, Clang is called from the directory containing
58 it. That allows the flags use relative file names within the project."
62 (defcustom company-clang-arguments nil
63 "A list of additional arguments to pass to clang when completing.
64 Prefix files (-include ...) can be selected with `company-clang-set-prefix'
65 or automatically through a custom `company-clang-prefix-guesser'."
66 :type '(repeat (string :tag "Argument")))
68 (defcustom company-clang-prefix-guesser 'company-clang-guess-prefix
69 "A function to determine the prefix file for the current buffer."
70 :type '(function :tag "Guesser function" nil))
72 (defvar company-clang-modes '(c-mode c++-mode objc-mode)
73 "Major modes which clang may complete.")
75 (defcustom company-clang-insert-arguments t
76 "When non-nil, insert function arguments as a template after completion."
78 :package-version '(company . "0.8.0"))
80 ;; prefix ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
82 (defvar company-clang--prefix nil)
84 (defsubst company-clang--guess-pch-file (file)
85 (let ((dir (directory-file-name (file-name-directory file))))
86 (when (equal (file-name-nondirectory dir) "Classes")
87 (setq dir (file-name-directory dir)))
88 (car (directory-files dir t "\\([^.]h\\|[^h]\\).pch\\'" t))))
90 (defsubst company-clang--file-substring (file beg end)
92 (insert-file-contents-literally file nil beg end)
95 (defun company-clang-guess-prefix ()
96 "Try to guess the prefix file for the current buffer."
97 ;; Prefixes seem to be called .pch. Pre-compiled headers do, too.
98 ;; So we look at the magic number to rule them out.
99 (let* ((file (company-clang--guess-pch-file buffer-file-name))
100 (magic-number (and file (company-clang--file-substring file 0 4))))
101 (unless (member magic-number '("CPCH" "gpch"))
104 (defun company-clang-set-prefix (&optional prefix)
105 "Use PREFIX as a prefix (-include ...) file for clang completion."
106 (interactive (let ((def (funcall company-clang-prefix-guesser)))
107 (unless (stringp def)
108 (setq def default-directory))
109 (list (read-file-name "Prefix file: "
110 (when def (file-name-directory def))
111 def t (when def (file-name-nondirectory def))))))
112 ;; TODO: pre-compile?
113 (setq company-clang--prefix (and (stringp prefix)
114 (file-regular-p prefix)
118 (add-hook 'kill-emacs-hook 'company-clang-set-prefix)
120 ;; parsing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
122 ;; Do we ever see OVERLOAD (or OVERRIDE)?
123 (defconst company-clang--completion-pattern
124 "^COMPLETION: \\_<\\(%s[a-zA-Z0-9_:]*\\|Pattern\\)\\(?:\\(?: (InBase)\\)? : \\(.*\\)$\\)?$")
126 (defconst company-clang--error-buffer-name "*clang-error*")
128 (defun company-clang--lang-option ()
129 (if (eq major-mode 'objc-mode)
130 (if (string= "m" (file-name-extension buffer-file-name))
131 "objective-c" "objective-c++")
132 (substring (symbol-name major-mode) 0 -5)))
134 (defun company-clang--parse-output (prefix _objc)
135 (goto-char (point-min))
136 (let ((pattern (format company-clang--completion-pattern
137 (regexp-quote prefix)))
138 (case-fold-search nil)
139 (results (make-hash-table :test 'equal :size (/ (point-max) 100)))
141 (while (re-search-forward pattern nil t)
142 (let ((match (match-string-no-properties 1))
143 (meta (match-string-no-properties 2)))
144 (when (equal match "Pattern")
145 (setq match (company-clang--pattern-to-match meta)))
146 (when (string-match ":" match)
147 (setq match (substring match 0 (match-beginning 0))))
148 ;; Avoiding duplicates:
149 ;; https://github.com/company-mode/company-mode/issues/841
151 ;; Either meta != completion (not a macro)
152 ((not (equal match meta))
153 (puthash match meta results))
154 ;; Or it's the first time we see this completion
155 ((eq (gethash match results 'none) 'none)
156 (puthash match nil results)))))
160 (put-text-property 0 1 'meta (company-clang--strip-formatting meta) match))
165 (defun company-clang--pattern-to-match (pat)
168 (when (string-match "#]" pat)
169 (setq start (match-end 0)))
170 (when (string-match "[ \(]<#" pat start)
171 (setq end (match-beginning 0)))
172 (substring pat start end)))
174 (defun company-clang--meta (candidate)
175 (get-text-property 0 'meta candidate))
177 (defun company-clang--annotation (candidate)
178 (let ((ann (company-clang--annotation-1 candidate)))
179 (if (not (and ann (string-prefix-p "(*)" ann)))
183 (search-backward ")")
184 (let ((pt (1+ (point))))
185 (re-search-forward ".\\_>" nil t)
186 (delete-region pt (point)))
189 ;; TODO: Parse the original formatting here, rather than guess.
190 ;; Strip it every time in the `meta' handler instead.
191 (defun company-clang--annotation-1 (candidate)
192 (let ((meta (company-clang--meta candidate)))
195 ((string-match "[^:]:[^:]" meta)
196 (substring meta (1+ (match-beginning 0))))
197 ((string-match "(anonymous)" meta) nil)
198 ((string-match "\\((.*)[ a-z]*\\'\\)" meta)
199 (let ((paren (match-beginning 1)))
200 (if (not (eq (aref meta (1- paren)) ?>))
201 (match-string 1 meta)
205 (substring meta (1- (search-backward "<"))))))))))
207 (defun company-clang--strip-formatting (text)
208 (replace-regexp-in-string
210 (replace-regexp-in-string "[<{[]#\\|#[>}]" "" text t)
213 (defun company-clang--handle-error (res args)
214 (goto-char (point-min))
215 (let* ((buf (get-buffer-create company-clang--error-buffer-name))
216 (cmd (concat company-clang-executable " " (mapconcat 'identity args " ")))
217 (pattern (format company-clang--completion-pattern ""))
218 (message-truncate-lines t)
219 (err (if (and (re-search-forward pattern nil t)
220 ;; Something in the Windows build?
221 ;; Looks like Clang doesn't always include the error text
222 ;; before completions (even if exited with error).
223 (> (match-beginning 0) (point-min)))
224 (buffer-substring-no-properties (point-min)
225 (1- (match-beginning 0)))
226 ;; Warn the user more aggressively if no match was found.
227 (message "clang failed with error %d: %s" res cmd)
230 (with-current-buffer buf
231 (let ((inhibit-read-only t))
233 (insert (current-time-string)
234 (format "\nclang failed with error %d:\n" res)
237 (setq buffer-read-only t)
238 (goto-char (point-min))))))
240 (defun company-clang--start-process (prefix callback &rest args)
241 (let* ((objc (derived-mode-p 'objc-mode))
242 (buf (get-buffer-create "*clang-output*"))
243 ;; Looks unnecessary in Emacs 25.1 and later.
244 ;; (Inconclusive, needs more testing):
245 ;; https://github.com/company-mode/company-mode/pull/288#issuecomment-72491808
246 (process-adaptive-read-buffering nil)
247 (existing-process (get-buffer-process buf)))
248 (when existing-process
249 (kill-process existing-process))
250 (with-current-buffer buf
252 (setq buffer-undo-list t))
253 (let* ((process-connection-type nil)
254 (process (apply #'start-file-process "company-clang" buf
255 company-clang-executable args)))
256 (set-process-sentinel
258 (lambda (proc status)
259 (unless (string-match-p "hangup\\|killed" status)
262 (let ((res (process-exit-status proc)))
263 (with-current-buffer buf
265 (company-clang--handle-error res args))
266 ;; Still try to get any useful input.
267 (company-clang--parse-output prefix objc)))))))
268 (unless (company-clang--auto-save-p)
269 (send-region process (point-min) (point-max))
270 (send-string process "\n")
271 (process-send-eof process)))))
273 (defsubst company-clang--build-location (pos)
277 (if (company-clang--auto-save-p) buffer-file-name "-")
280 (encode-coding-region
281 (line-beginning-position)
286 (defsubst company-clang--build-complete-args (pos)
287 (append '("-fsyntax-only" "-Xclang" "-code-completion-macros")
288 (unless (company-clang--auto-save-p)
289 (list "-x" (company-clang--lang-option)))
290 (company-clang--arguments)
291 (when (stringp company-clang--prefix)
292 (list "-include" (expand-file-name company-clang--prefix)))
293 (list "-Xclang" (format "-code-completion-at=%s"
294 (company-clang--build-location pos)))
295 (list (if (company-clang--auto-save-p) buffer-file-name "-"))))
297 (defun company-clang--arguments ()
298 (let ((fname "compile_flags.txt")
299 (args company-clang-arguments)
301 (when company-clang-use-compile-flags-txt
302 (let ((dir (locate-dominating-file default-directory fname)))
304 (setq current-dir-rel (file-relative-name default-directory dir))
305 (setq default-directory dir)
307 (insert-file-contents fname)
311 (split-string (buffer-substring-no-properties
312 (point-min) (point-max))
316 (unless (equal current-dir-rel "./")
317 (push (format "-I%s" current-dir-rel) args)))))
320 (defun company-clang--candidates (prefix callback)
321 (and (company-clang--auto-save-p)
324 (when (null company-clang--prefix)
325 (company-clang-set-prefix (or (funcall company-clang-prefix-guesser)
327 (let ((default-directory default-directory))
328 (apply 'company-clang--start-process
331 (company-clang--build-complete-args
332 (if (company-clang--check-version 4.0 9.0)
334 (- (point) (length prefix)))))))
336 (defun company-clang--prefix ()
337 (if company-clang-begin-after-member-access
338 (company-grab-symbol-cons "\\.\\|->\\|::" 2)
339 (company-grab-symbol)))
341 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
343 (defconst company-clang-required-version 1.1)
345 (defvar company-clang--version nil)
347 (defun company-clang--auto-save-p ()
349 (company-clang--check-version 2.9 3.1)))
351 (defun company-clang--check-version (min apple-min)
352 (pcase-exhaustive company-clang--version
353 (`(apple . ,ver) (>= ver apple-min))
354 (`(normal . ,ver) (>= ver min))))
356 (defsubst company-clang-version ()
357 "Return the version of `company-clang-executable'."
359 (call-process company-clang-executable nil t nil "--version")
360 (goto-char (point-min))
361 (if (re-search-forward
362 "\\(clang\\|Apple LLVM\\|bcc32x\\|bcc64\\) version \\([0-9.]+\\)" nil t)
364 (if (equal (match-string-no-properties 1) "Apple LLVM")
367 (string-to-number (match-string-no-properties 2)))
370 (defun company-clang (command &optional arg &rest _ignored)
371 "`company-mode' completion backend for Clang.
372 Clang is a parser for C and ObjC. Clang version 1.1 or newer is required.
374 Additional command line arguments can be specified in
375 `company-clang-arguments'. Prefix files (-include ...) can be selected
376 with `company-clang-set-prefix' or automatically through a custom
377 `company-clang-prefix-guesser'.
379 With Clang versions before 2.9, we have to save the buffer before
380 performing completion. With Clang 2.9 and later, buffer contents are
381 passed via standard input."
382 (interactive (list 'interactive))
384 (interactive (company-begin-backend 'company-clang))
385 (init (when (memq major-mode company-clang-modes)
386 (unless company-clang-executable
387 (error "Company found no clang executable"))
388 (setq company-clang--version (company-clang-version))
389 (unless (company-clang--check-version
390 company-clang-required-version
391 company-clang-required-version)
392 (error "Company requires clang version %s"
393 company-clang-required-version))))
394 (prefix (and (memq major-mode company-clang-modes)
396 company-clang-executable
397 (not (company-in-string-or-comment))
398 (or (company-clang--prefix) 'stop)))
399 (candidates (cons :async
400 (lambda (cb) (company-clang--candidates arg cb))))
401 (meta (company-clang--meta arg))
402 (kind (company-clang--kind arg))
403 (annotation (company-clang--annotation arg))
404 (post-completion (let ((anno (company-clang--annotation arg)))
405 (when (and company-clang-insert-arguments anno)
407 (if (string-match "\\`:[^:]" anno)
408 (company-template-objc-templatify anno)
409 (company-template-c-like-templatify
410 (concat arg anno))))))))
412 (defun company-clang--kind (arg)
413 ;; XXX: Not very precise.
414 ;; E.g. it will say that an arg-less ObjC method is a variable (perhaps we
415 ;; could look around for brackets, etc, if there any actual users who's
417 ;; And we can't distinguish between local vars and struct fields.
418 ;; Or between keywords and macros.
419 (let ((meta (company-clang--meta arg)))
421 ((null meta) 'keyword)
422 ((string-match "(" meta)
423 (if (string-match-p (format "\\`%s *\\'" (regexp-quote arg))
424 (substring meta 0 (match-beginning 0)))
425 'keyword ; Also macro, actually (no return type).
429 (provide 'company-clang)
430 ;;; company-clang.el ends here