Klimi's new dotfiles with stow.
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.

350 lines
13 KiB

4 years ago
  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-arguments nil
  37. "Additional arguments to pass to clang when completing.
  38. Prefix files (-include ...) can be selected with `company-clang-set-prefix'
  39. or automatically through a custom `company-clang-prefix-guesser'."
  40. :type '(repeat (string :tag "Argument")))
  41. (defcustom company-clang-prefix-guesser 'company-clang-guess-prefix
  42. "A function to determine the prefix file for the current buffer."
  43. :type '(function :tag "Guesser function" nil))
  44. (defvar company-clang-modes '(c-mode c++-mode objc-mode)
  45. "Major modes which clang may complete.")
  46. (defcustom company-clang-insert-arguments t
  47. "When non-nil, insert function arguments as a template after completion."
  48. :type 'boolean
  49. :package-version '(company . "0.8.0"))
  50. ;; prefix ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  51. (defvar company-clang--prefix nil)
  52. (defsubst company-clang--guess-pch-file (file)
  53. (let ((dir (directory-file-name (file-name-directory file))))
  54. (when (equal (file-name-nondirectory dir) "Classes")
  55. (setq dir (file-name-directory dir)))
  56. (car (directory-files dir t "\\([^.]h\\|[^h]\\).pch\\'" t))))
  57. (defsubst company-clang--file-substring (file beg end)
  58. (with-temp-buffer
  59. (insert-file-contents-literally file nil beg end)
  60. (buffer-string)))
  61. (defun company-clang-guess-prefix ()
  62. "Try to guess the prefix file for the current buffer."
  63. ;; Prefixes seem to be called .pch. Pre-compiled headers do, too.
  64. ;; So we look at the magic number to rule them out.
  65. (let* ((file (company-clang--guess-pch-file buffer-file-name))
  66. (magic-number (and file (company-clang--file-substring file 0 4))))
  67. (unless (member magic-number '("CPCH" "gpch"))
  68. file)))
  69. (defun company-clang-set-prefix (&optional prefix)
  70. "Use PREFIX as a prefix (-include ...) file for clang completion."
  71. (interactive (let ((def (funcall company-clang-prefix-guesser)))
  72. (unless (stringp def)
  73. (setq def default-directory))
  74. (list (read-file-name "Prefix file: "
  75. (when def (file-name-directory def))
  76. def t (when def (file-name-nondirectory def))))))
  77. ;; TODO: pre-compile?
  78. (setq company-clang--prefix (and (stringp prefix)
  79. (file-regular-p prefix)
  80. prefix)))
  81. ;; Clean-up on exit.
  82. (add-hook 'kill-emacs-hook 'company-clang-set-prefix)
  83. ;; parsing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  84. ;; TODO: Handle Pattern (syntactic hints would be neat).
  85. ;; Do we ever see OVERLOAD (or OVERRIDE)?
  86. (defconst company-clang--completion-pattern
  87. "^COMPLETION: \\_<\\(%s[a-zA-Z0-9_:]*\\)\\(?:\\(?: (InBase)\\)? : \\(.*\\)$\\)?$")
  88. (defconst company-clang--error-buffer-name "*clang-error*")
  89. (defun company-clang--lang-option ()
  90. (if (eq major-mode 'objc-mode)
  91. (if (string= "m" (file-name-extension buffer-file-name))
  92. "objective-c" "objective-c++")
  93. (substring (symbol-name major-mode) 0 -5)))
  94. (defun company-clang--parse-output (prefix _objc)
  95. (goto-char (point-min))
  96. (let ((pattern (format company-clang--completion-pattern
  97. (regexp-quote prefix)))
  98. (case-fold-search nil)
  99. lines match)
  100. (while (re-search-forward pattern nil t)
  101. (setq match (match-string-no-properties 1))
  102. (unless (equal match "Pattern")
  103. (save-match-data
  104. (when (string-match ":" match)
  105. (setq match (substring match 0 (match-beginning 0)))))
  106. (let ((meta (match-string-no-properties 2)))
  107. (when (and meta (not (string= match meta)))
  108. (put-text-property 0 1 'meta
  109. (company-clang--strip-formatting meta)
  110. match)))
  111. (push match lines)))
  112. lines))
  113. (defun company-clang--meta (candidate)
  114. (get-text-property 0 'meta candidate))
  115. (defun company-clang--annotation (candidate)
  116. (let ((ann (company-clang--annotation-1 candidate)))
  117. (if (not (and ann (string-prefix-p "(*)" ann)))
  118. ann
  119. (with-temp-buffer
  120. (insert ann)
  121. (search-backward ")")
  122. (let ((pt (1+ (point))))
  123. (re-search-forward ".\\_>" nil t)
  124. (delete-region pt (point)))
  125. (buffer-string)))))
  126. (defun company-clang--annotation-1 (candidate)
  127. (let ((meta (company-clang--meta candidate)))
  128. (cond
  129. ((null meta) nil)
  130. ((string-match "[^:]:[^:]" meta)
  131. (substring meta (1+ (match-beginning 0))))
  132. ((string-match "(anonymous)" meta) nil)
  133. ((string-match "\\((.*)[ a-z]*\\'\\)" meta)
  134. (let ((paren (match-beginning 1)))
  135. (if (not (eq (aref meta (1- paren)) ?>))
  136. (match-string 1 meta)
  137. (with-temp-buffer
  138. (insert meta)
  139. (goto-char paren)
  140. (substring meta (1- (search-backward "<"))))))))))
  141. (defun company-clang--strip-formatting (text)
  142. (replace-regexp-in-string
  143. "#]" " "
  144. (replace-regexp-in-string "[<{[]#\\|#[>}]" "" text t)
  145. t))
  146. (defun company-clang--handle-error (res args)
  147. (goto-char (point-min))
  148. (let* ((buf (get-buffer-create company-clang--error-buffer-name))
  149. (cmd (concat company-clang-executable " " (mapconcat 'identity args " ")))
  150. (pattern (format company-clang--completion-pattern ""))
  151. (message-truncate-lines t)
  152. (err (if (re-search-forward pattern nil t)
  153. (buffer-substring-no-properties (point-min)
  154. (1- (match-beginning 0)))
  155. ;; Warn the user more aggressively if no match was found.
  156. (message "clang failed with error %d: %s" res cmd)
  157. (buffer-string))))
  158. (with-current-buffer buf
  159. (let ((inhibit-read-only t))
  160. (erase-buffer)
  161. (insert (current-time-string)
  162. (format "\nclang failed with error %d:\n" res)
  163. cmd "\n\n")
  164. (insert err)
  165. (setq buffer-read-only t)
  166. (goto-char (point-min))))))
  167. (defun company-clang--start-process (prefix callback &rest args)
  168. (let* ((objc (derived-mode-p 'objc-mode))
  169. (buf (get-buffer-create "*clang-output*"))
  170. ;; Looks unnecessary in Emacs 25.1 and later.
  171. (process-adaptive-read-buffering nil)
  172. (existing-process (get-buffer-process buf)))
  173. (when existing-process
  174. (kill-process existing-process))
  175. (with-current-buffer buf
  176. (erase-buffer)
  177. (setq buffer-undo-list t))
  178. (let* ((process-connection-type nil)
  179. (process (apply #'start-file-process "company-clang" buf
  180. company-clang-executable args)))
  181. (set-process-sentinel
  182. process
  183. (lambda (proc status)
  184. (unless (string-match-p "hangup\\|killed" status)
  185. (funcall
  186. callback
  187. (let ((res (process-exit-status proc)))
  188. (with-current-buffer buf
  189. (unless (eq 0 res)
  190. (company-clang--handle-error res args))
  191. ;; Still try to get any useful input.
  192. (company-clang--parse-output prefix objc)))))))
  193. (unless (company-clang--auto-save-p)
  194. (send-region process (point-min) (point-max))
  195. (send-string process "\n")
  196. (process-send-eof process)))))
  197. (defsubst company-clang--build-location (pos)
  198. (save-excursion
  199. (goto-char pos)
  200. (format "%s:%d:%d"
  201. (if (company-clang--auto-save-p) buffer-file-name "-")
  202. (line-number-at-pos)
  203. (1+ (length
  204. (encode-coding-region
  205. (line-beginning-position)
  206. (point)
  207. 'utf-8
  208. t))))))
  209. (defsubst company-clang--build-complete-args (pos)
  210. (append '("-fsyntax-only" "-Xclang" "-code-completion-macros")
  211. (unless (company-clang--auto-save-p)
  212. (list "-x" (company-clang--lang-option)))
  213. company-clang-arguments
  214. (when (stringp company-clang--prefix)
  215. (list "-include" (expand-file-name company-clang--prefix)))
  216. (list "-Xclang" (format "-code-completion-at=%s"
  217. (company-clang--build-location pos)))
  218. (list (if (company-clang--auto-save-p) buffer-file-name "-"))))
  219. (defun company-clang--candidates (prefix callback)
  220. (and (company-clang--auto-save-p)
  221. (buffer-modified-p)
  222. (basic-save-buffer))
  223. (when (null company-clang--prefix)
  224. (company-clang-set-prefix (or (funcall company-clang-prefix-guesser)
  225. 'none)))
  226. (apply 'company-clang--start-process
  227. prefix
  228. callback
  229. (company-clang--build-complete-args
  230. (if (company-clang--check-version 4.0 9.0)
  231. (point)
  232. (- (point) (length prefix))))))
  233. (defun company-clang--prefix ()
  234. (if company-clang-begin-after-member-access
  235. (company-grab-symbol-cons "\\.\\|->\\|::" 2)
  236. (company-grab-symbol)))
  237. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  238. (defconst company-clang-required-version 1.1)
  239. (defvar company-clang--version nil)
  240. (defun company-clang--auto-save-p ()
  241. (not
  242. (company-clang--check-version 2.9 3.1)))
  243. (defun company-clang--check-version (min apple-min)
  244. (pcase company-clang--version
  245. (`(apple . ,ver) (>= ver apple-min))
  246. (`(normal . ,ver) (>= ver min))
  247. (_ (error "pcase-exhaustive is not in Emacs 24.3!"))))
  248. (defsubst company-clang-version ()
  249. "Return the version of `company-clang-executable'."
  250. (with-temp-buffer
  251. (call-process company-clang-executable nil t nil "--version")
  252. (goto-char (point-min))
  253. (if (re-search-forward "\\(clang\\|Apple LLVM\\) version \\([0-9.]+\\)" nil t)
  254. (cons
  255. (if (equal (match-string-no-properties 1) "Apple LLVM")
  256. 'apple
  257. 'normal)
  258. (string-to-number (match-string-no-properties 2)))
  259. 0)))
  260. (defun company-clang (command &optional arg &rest ignored)
  261. "`company-mode' completion backend for Clang.
  262. Clang is a parser for C and ObjC. Clang version 1.1 or newer is required.
  263. Additional command line arguments can be specified in
  264. `company-clang-arguments'. Prefix files (-include ...) can be selected
  265. with `company-clang-set-prefix' or automatically through a custom
  266. `company-clang-prefix-guesser'.
  267. With Clang versions before 2.9, we have to save the buffer before
  268. performing completion. With Clang 2.9 and later, buffer contents are
  269. passed via standard input."
  270. (interactive (list 'interactive))
  271. (cl-case command
  272. (interactive (company-begin-backend 'company-clang))
  273. (init (when (memq major-mode company-clang-modes)
  274. (unless company-clang-executable
  275. (error "Company found no clang executable"))
  276. (setq company-clang--version (company-clang-version))
  277. (unless (company-clang--check-version
  278. company-clang-required-version
  279. company-clang-required-version)
  280. (error "Company requires clang version %s"
  281. company-clang-required-version))))
  282. (prefix (and (memq major-mode company-clang-modes)
  283. buffer-file-name
  284. company-clang-executable
  285. (not (company-in-string-or-comment))
  286. (or (company-clang--prefix) 'stop)))
  287. (candidates (cons :async
  288. (lambda (cb) (company-clang--candidates arg cb))))
  289. (meta (company-clang--meta arg))
  290. (annotation (company-clang--annotation arg))
  291. (post-completion (let ((anno (company-clang--annotation arg)))
  292. (when (and company-clang-insert-arguments anno)
  293. (insert anno)
  294. (if (string-match "\\`:[^:]" anno)
  295. (company-template-objc-templatify anno)
  296. (company-template-c-like-templatify
  297. (concat arg anno))))))))
  298. (provide 'company-clang)
  299. ;;; company-clang.el ends here