Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

389 lines
15 KiB

  1. ;;; company-clang.el --- company-mode completion backend for Clang -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2009, 2011, 2013-2019 Free Software Foundation, Inc.
  3. ;; Author: Nikolaj Schumacher
  4. ;; This file is part of GNU Emacs.
  5. ;; GNU Emacs is free software: you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; GNU Emacs is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;;
  17. ;;; Code:
  18. (require 'company)
  19. (require 'company-template)
  20. (require 'cl-lib)
  21. (defgroup company-clang nil
  22. "Completion backend for Clang."
  23. :group 'company)
  24. (defcustom company-clang-executable
  25. (executable-find "clang")
  26. "Location of clang executable."
  27. :type 'file)
  28. (defcustom company-clang-begin-after-member-access t
  29. "When non-nil, automatic completion will start whenever the current
  30. symbol is preceded by \".\", \"->\" or \"::\", ignoring
  31. `company-minimum-prefix-length'.
  32. If `company-begin-commands' is a list, it should include `c-electric-lt-gt'
  33. and `c-electric-colon', for automatic completion right after \">\" and
  34. \":\"."
  35. :type 'boolean)
  36. (defcustom company-clang-use-compile-flags-txt nil
  37. "When non-nil, use flags from compile_flags.txt if present.
  38. The lines from that files will be appended to `company-clang-arguments'.
  39. And if such file is found, Clang is called from the directory containing
  40. it. That allows the flags use relative file names within the project."
  41. :type 'boolean
  42. :safe 'booleanp)
  43. (defcustom company-clang-arguments nil
  44. "Additional arguments to pass to clang when completing.
  45. Prefix files (-include ...) can be selected with `company-clang-set-prefix'
  46. or automatically through a custom `company-clang-prefix-guesser'."
  47. :type '(repeat (string :tag "Argument")))
  48. (defcustom company-clang-prefix-guesser 'company-clang-guess-prefix
  49. "A function to determine the prefix file for the current buffer."
  50. :type '(function :tag "Guesser function" nil))
  51. (defvar company-clang-modes '(c-mode c++-mode objc-mode)
  52. "Major modes which clang may complete.")
  53. (defcustom company-clang-insert-arguments t
  54. "When non-nil, insert function arguments as a template after completion."
  55. :type 'boolean
  56. :package-version '(company . "0.8.0"))
  57. ;; prefix ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  58. (defvar company-clang--prefix nil)
  59. (defsubst company-clang--guess-pch-file (file)
  60. (let ((dir (directory-file-name (file-name-directory file))))
  61. (when (equal (file-name-nondirectory dir) "Classes")
  62. (setq dir (file-name-directory dir)))
  63. (car (directory-files dir t "\\([^.]h\\|[^h]\\).pch\\'" t))))
  64. (defsubst company-clang--file-substring (file beg end)
  65. (with-temp-buffer
  66. (insert-file-contents-literally file nil beg end)
  67. (buffer-string)))
  68. (defun company-clang-guess-prefix ()
  69. "Try to guess the prefix file for the current buffer."
  70. ;; Prefixes seem to be called .pch. Pre-compiled headers do, too.
  71. ;; So we look at the magic number to rule them out.
  72. (let* ((file (company-clang--guess-pch-file buffer-file-name))
  73. (magic-number (and file (company-clang--file-substring file 0 4))))
  74. (unless (member magic-number '("CPCH" "gpch"))
  75. file)))
  76. (defun company-clang-set-prefix (&optional prefix)
  77. "Use PREFIX as a prefix (-include ...) file for clang completion."
  78. (interactive (let ((def (funcall company-clang-prefix-guesser)))
  79. (unless (stringp def)
  80. (setq def default-directory))
  81. (list (read-file-name "Prefix file: "
  82. (when def (file-name-directory def))
  83. def t (when def (file-name-nondirectory def))))))
  84. ;; TODO: pre-compile?
  85. (setq company-clang--prefix (and (stringp prefix)
  86. (file-regular-p prefix)
  87. prefix)))
  88. ;; Clean-up on exit.
  89. (add-hook 'kill-emacs-hook 'company-clang-set-prefix)
  90. ;; parsing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  91. ;; TODO: Handle Pattern (syntactic hints would be neat).
  92. ;; Do we ever see OVERLOAD (or OVERRIDE)?
  93. (defconst company-clang--completion-pattern
  94. "^COMPLETION: \\_<\\(%s[a-zA-Z0-9_:]*\\)\\(?:\\(?: (InBase)\\)? : \\(.*\\)$\\)?$")
  95. (defconst company-clang--error-buffer-name "*clang-error*")
  96. (defun company-clang--lang-option ()
  97. (if (eq major-mode 'objc-mode)
  98. (if (string= "m" (file-name-extension buffer-file-name))
  99. "objective-c" "objective-c++")
  100. (substring (symbol-name major-mode) 0 -5)))
  101. (defun company-clang--parse-output (prefix _objc)
  102. (goto-char (point-min))
  103. (let ((pattern (format company-clang--completion-pattern
  104. (regexp-quote prefix)))
  105. (case-fold-search nil)
  106. lines match)
  107. (while (re-search-forward pattern nil t)
  108. (setq match (match-string-no-properties 1))
  109. (unless (equal match "Pattern")
  110. (save-match-data
  111. (when (string-match ":" match)
  112. (setq match (substring match 0 (match-beginning 0)))))
  113. (let ((meta (match-string-no-properties 2)))
  114. (when (and meta (not (string= match meta)))
  115. (put-text-property 0 1 'meta
  116. (company-clang--strip-formatting meta)
  117. match)))
  118. (push match lines)))
  119. lines))
  120. (defun company-clang--meta (candidate)
  121. (get-text-property 0 'meta candidate))
  122. (defun company-clang--annotation (candidate)
  123. (let ((ann (company-clang--annotation-1 candidate)))
  124. (if (not (and ann (string-prefix-p "(*)" ann)))
  125. ann
  126. (with-temp-buffer
  127. (insert ann)
  128. (search-backward ")")
  129. (let ((pt (1+ (point))))
  130. (re-search-forward ".\\_>" nil t)
  131. (delete-region pt (point)))
  132. (buffer-string)))))
  133. (defun company-clang--annotation-1 (candidate)
  134. (let ((meta (company-clang--meta candidate)))
  135. (cond
  136. ((null meta) nil)
  137. ((string-match "[^:]:[^:]" meta)
  138. (substring meta (1+ (match-beginning 0))))
  139. ((string-match "(anonymous)" meta) nil)
  140. ((string-match "\\((.*)[ a-z]*\\'\\)" meta)
  141. (let ((paren (match-beginning 1)))
  142. (if (not (eq (aref meta (1- paren)) ?>))
  143. (match-string 1 meta)
  144. (with-temp-buffer
  145. (insert meta)
  146. (goto-char paren)
  147. (substring meta (1- (search-backward "<"))))))))))
  148. (defun company-clang--strip-formatting (text)
  149. (replace-regexp-in-string
  150. "#]" " "
  151. (replace-regexp-in-string "[<{[]#\\|#[>}]" "" text t)
  152. t))
  153. (defun company-clang--handle-error (res args)
  154. (goto-char (point-min))
  155. (let* ((buf (get-buffer-create company-clang--error-buffer-name))
  156. (cmd (concat company-clang-executable " " (mapconcat 'identity args " ")))
  157. (pattern (format company-clang--completion-pattern ""))
  158. (message-truncate-lines t)
  159. (err (if (and (re-search-forward pattern nil t)
  160. ;; Something in the Windows build?
  161. ;; Looks like Clang doesn't always include the error text
  162. ;; before completions (even if exited with error).
  163. (> (match-beginning 0) (point-min)))
  164. (buffer-substring-no-properties (point-min)
  165. (1- (match-beginning 0)))
  166. ;; Warn the user more aggressively if no match was found.
  167. (message "clang failed with error %d: %s" res cmd)
  168. (buffer-string))))
  169. (with-current-buffer buf
  170. (let ((inhibit-read-only t))
  171. (erase-buffer)
  172. (insert (current-time-string)
  173. (format "\nclang failed with error %d:\n" res)
  174. cmd "\n\n")
  175. (insert err)
  176. (setq buffer-read-only t)
  177. (goto-char (point-min))))))
  178. (defun company-clang--start-process (prefix callback &rest args)
  179. (let* ((objc (derived-mode-p 'objc-mode))
  180. (buf (get-buffer-create "*clang-output*"))
  181. ;; Looks unnecessary in Emacs 25.1 and later.
  182. (process-adaptive-read-buffering nil)
  183. (existing-process (get-buffer-process buf)))
  184. (when existing-process
  185. (kill-process existing-process))
  186. (with-current-buffer buf
  187. (erase-buffer)
  188. (setq buffer-undo-list t))
  189. (let* ((process-connection-type nil)
  190. (process (apply #'start-file-process "company-clang" buf
  191. company-clang-executable args)))
  192. (set-process-sentinel
  193. process
  194. (lambda (proc status)
  195. (unless (string-match-p "hangup\\|killed" status)
  196. (funcall
  197. callback
  198. (let ((res (process-exit-status proc)))
  199. (with-current-buffer buf
  200. (unless (eq 0 res)
  201. (company-clang--handle-error res args))
  202. ;; Still try to get any useful input.
  203. (company-clang--parse-output prefix objc)))))))
  204. (unless (company-clang--auto-save-p)
  205. (send-region process (point-min) (point-max))
  206. (send-string process "\n")
  207. (process-send-eof process)))))
  208. (defsubst company-clang--build-location (pos)
  209. (save-excursion
  210. (goto-char pos)
  211. (format "%s:%d:%d"
  212. (if (company-clang--auto-save-p) buffer-file-name "-")
  213. (line-number-at-pos)
  214. (1+ (length
  215. (encode-coding-region
  216. (line-beginning-position)
  217. (point)
  218. 'utf-8
  219. t))))))
  220. (defsubst company-clang--build-complete-args (pos)
  221. (append '("-fsyntax-only" "-Xclang" "-code-completion-macros")
  222. (unless (company-clang--auto-save-p)
  223. (list "-x" (company-clang--lang-option)))
  224. (company-clang--arguments)
  225. (when (stringp company-clang--prefix)
  226. (list "-include" (expand-file-name company-clang--prefix)))
  227. (list "-Xclang" (format "-code-completion-at=%s"
  228. (company-clang--build-location pos)))
  229. (list (if (company-clang--auto-save-p) buffer-file-name "-"))))
  230. (defun company-clang--arguments ()
  231. (let ((fname "compile_flags.txt")
  232. (args company-clang-arguments)
  233. current-dir-rel)
  234. (when company-clang-use-compile-flags-txt
  235. (let ((dir (locate-dominating-file default-directory fname)))
  236. (when dir
  237. (setq current-dir-rel (file-relative-name default-directory dir))
  238. (setq default-directory dir)
  239. (with-temp-buffer
  240. (insert-file-contents fname)
  241. (setq args
  242. (append
  243. args
  244. (split-string (buffer-substring-no-properties
  245. (point-min) (point-max))
  246. "[\n\r]+"
  247. t
  248. "[ \t]+"))))
  249. (unless (equal current-dir-rel "./")
  250. (push (format "-I%s" current-dir-rel) args)))))
  251. args))
  252. (defun company-clang--candidates (prefix callback)
  253. (and (company-clang--auto-save-p)
  254. (buffer-modified-p)
  255. (basic-save-buffer))
  256. (when (null company-clang--prefix)
  257. (company-clang-set-prefix (or (funcall company-clang-prefix-guesser)
  258. 'none)))
  259. (let ((default-directory default-directory))
  260. (apply 'company-clang--start-process
  261. prefix
  262. callback
  263. (company-clang--build-complete-args
  264. (if (company-clang--check-version 4.0 9.0)
  265. (point)
  266. (- (point) (length prefix)))))))
  267. (defun company-clang--prefix ()
  268. (if company-clang-begin-after-member-access
  269. (company-grab-symbol-cons "\\.\\|->\\|::" 2)
  270. (company-grab-symbol)))
  271. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  272. (defconst company-clang-required-version 1.1)
  273. (defvar company-clang--version nil)
  274. (defun company-clang--auto-save-p ()
  275. (not
  276. (company-clang--check-version 2.9 3.1)))
  277. (defun company-clang--check-version (min apple-min)
  278. (pcase company-clang--version
  279. (`(apple . ,ver) (>= ver apple-min))
  280. (`(normal . ,ver) (>= ver min))
  281. (_ (error "pcase-exhaustive is not in Emacs 24.3!"))))
  282. (defsubst company-clang-version ()
  283. "Return the version of `company-clang-executable'."
  284. (with-temp-buffer
  285. (call-process company-clang-executable nil t nil "--version")
  286. (goto-char (point-min))
  287. (if (re-search-forward
  288. "\\(clang\\|Apple LLVM\\|bcc32x\\|bcc64\\) version \\([0-9.]+\\)" nil t)
  289. (cons
  290. (if (equal (match-string-no-properties 1) "Apple LLVM")
  291. 'apple
  292. 'normal)
  293. (string-to-number (match-string-no-properties 2)))
  294. 0)))
  295. (defun company-clang (command &optional arg &rest ignored)
  296. "`company-mode' completion backend for Clang.
  297. Clang is a parser for C and ObjC. Clang version 1.1 or newer is required.
  298. Additional command line arguments can be specified in
  299. `company-clang-arguments'. Prefix files (-include ...) can be selected
  300. with `company-clang-set-prefix' or automatically through a custom
  301. `company-clang-prefix-guesser'.
  302. With Clang versions before 2.9, we have to save the buffer before
  303. performing completion. With Clang 2.9 and later, buffer contents are
  304. passed via standard input."
  305. (interactive (list 'interactive))
  306. (cl-case command
  307. (interactive (company-begin-backend 'company-clang))
  308. (init (when (memq major-mode company-clang-modes)
  309. (unless company-clang-executable
  310. (error "Company found no clang executable"))
  311. (setq company-clang--version (company-clang-version))
  312. (unless (company-clang--check-version
  313. company-clang-required-version
  314. company-clang-required-version)
  315. (error "Company requires clang version %s"
  316. company-clang-required-version))))
  317. (prefix (and (memq major-mode company-clang-modes)
  318. buffer-file-name
  319. company-clang-executable
  320. (not (company-in-string-or-comment))
  321. (or (company-clang--prefix) 'stop)))
  322. (candidates (cons :async
  323. (lambda (cb) (company-clang--candidates arg cb))))
  324. (meta (company-clang--meta arg))
  325. (annotation (company-clang--annotation arg))
  326. (post-completion (let ((anno (company-clang--annotation arg)))
  327. (when (and company-clang-insert-arguments anno)
  328. (insert anno)
  329. (if (string-match "\\`:[^:]" anno)
  330. (company-template-objc-templatify anno)
  331. (company-template-c-like-templatify
  332. (concat arg anno))))))))
  333. (provide 'company-clang)
  334. ;;; company-clang.el ends here