FG42/conf/emacs.d/auto-complete-clang.el

152 lines
5.5 KiB
EmacsLisp

(eval-when-compile (require 'cl))
(defvar clang-executable "clang")
(defvar clang-completion-doc-table (make-hash-table :test 'equal))
;; faces
(defface clang-completion-plain-face
'((t (:inherit default :family "Verdana")))
"clang completion hint base font" :group 'clang-completion-faces)
(defface clang-completion-type-face
'((t (:inherit 'clang-completion-plain-face :foreground "#729FCF" :weight bold :family "Verdana")))
"clang completion hint font for types" :group 'clang-completion-faces)
(defface clang-completion-variable-face
'((t (:inherit 'clang-completion-plain-face :foreground "#73D216" :family "Verdana")))
"clang completion hint font for variables" :group 'clang-completion-faces)
;; extra flags
(defvar clang-completion-pch nil)
(defvar clang-completion-flags nil)
(defvar clang-completion-suppress-error nil)
(defun clang-process-exec (command)
(with-output-to-string
(with-current-buffer standard-output
(unless (or (eq (apply 'call-process (car command) nil '(t ".clang-completion-error") nil (cdr command)) 0) clang-completion-suppress-error)
(let ((last-command compile-command))
(compile "cat .clang-completion-error")
(setq compile-command last-command))))))
(defun clang-parse-completion-line (line)
(cond ((string-match "^COMPLETION: Pattern" line) nil) ;; exclude patterns
((string-match "^COMPLETION: \\([^ ]*\\)\\(?: : \\([^\"]*\\)\\)$" line)
(list (match-string 1 line) (match-string 2 line)))
((string-match "^OVERRIDE: \\([^ ]*\\)\\(?: : \\([^\"]*\\)\\)$" line)
(list (match-string 1 line) (match-string 2 line)))
(t nil))
)
(defun clang-process (buffer point)
(unless (buffer-file-name buffer)
(return ""))
(let* ((filename (buffer-file-name buffer))
(col (1+ (- point (point-at-bol))))
(row (count-lines point (point-min)))
(cmd (list clang-executable "-cc1"
filename "-fsyntax-only" "-code-completion-at"
(format "%s:%s:%s" filename row col))))
;; eval the config file under buffer locations
(let* ((filedir (file-name-directory filename))
(config-filename (concat filedir ".clang-completion-config.el")))
(when (file-readable-p config-filename)
(with-temp-buffer
(insert-file-contents config-filename)
(eval-buffer))))
(when (listp clang-completion-flags)
(setq cmd (append cmd clang-completion-flags)))
(when (stringp clang-completion-pch)
(setq cmd (append cmd (list "-include-pch" clang-completion-pch))))
(message (format "complete at %s:%s:%s" filename row col))
(clang-process-exec cmd)))
(defun clang-get-process-result (string)
(let* ((completion-lines (split-string string "\n")))
(delq nil (mapcar 'clang-parse-completion-line completion-lines))))
(defun clang-get-process-completion-result (string)
(mapcar 'car (clang-get-process-result string)))
(defun clang-get-process-prototype-table (string)
(let* ((lines (clang-get-process-result string))
(result-table (make-hash-table :test 'equal)))
(dolist (line lines)
(let* ((key (first line))
(value (gethash key result-table)))
(setq value (append value (list (second line))))
(puthash key value result-table))
)
(setq clang-completion-doc-table result-table)))
(defun clang-get-completions (&optional buffer point)
;; save all modified buffers
(or buffer (setq buffer (current-buffer)))
(or point (setq point (point)))
(save-some-buffers t)
(let* ((output (clang-process buffer point)))
(clang-get-process-prototype-table output)
(clang-get-process-completion-result output)))
(defun filter-doc-buffer ()
(while (re-search-backward "\\[#.*?::#\\]" nil t)
(replace-match ""))
(goto-char (point-max))
(while (re-search-backward "\\[#\\|#\\]" nil t)
(replace-match " "))
(goto-char (point-max))
(while (re-search-backward "{#\\|#}\\|<#\\|#>" nil t)
(replace-match ""))
)
(defun clang-get-doc (symbol)
;;(setq symbol (symbol-name (intern-soft symbol)))
(let ((reslist (gethash symbol clang-completion-doc-table)))
(with-temp-buffer
(font-lock-add-keywords nil '(("\\[#\\(.*?\\)#\\]" 1
'clang-completion-type-face t)))
(font-lock-add-keywords nil '(("<#\\(.*?\\)#>" 1
'clang-completion-variable-face t)))
(font-lock-add-keywords nil '(("\\(.*\\)" 1
'clang-completion-plain-face t)))
(font-lock-mode t)
(insert (reduce '(lambda (x y) (concat x "\n" y)) reslist))
(font-lock-fontify-buffer)
(filter-doc-buffer)
(message (buffer-string))))
;;(with-temp-buffer
;; (dolist (proto reslist)
;; (insert proto)
;; (insert "\n\n"))
;; (filter-doc-buffer)
;; (buffer-string))
;; display nothing
(return nil))
(defvar ac-source-clang-complete
'((candidates . (clang-get-completions nil ac-point))
(prefix "[^a-zA-Z0-9_]\\(\\(?:[a-zA-Z_][a-zA-Z0-9_]*\\)?\\)" nil 1)
(document . clang-get-doc)
(requires . 0)
(symbol . "C")
(cache)))
;;(defvar ac-source-clang-static-complete
;; '((candidates . (clang-get-completions nil ac-point))
;; (prefix "::\\(\\(?:[a-zA-Z_][a-zA-Z0-9_]*\\)?\\)" nil 1)
;; ;;(document . 'clang-get-doc)
;; (requires . 0)
;; (symbol . "M")
;; (cache)))
(defun ac-complete-clang ()
(interactive)
(auto-complete '(ac-source-clang-complete)))
(provide 'auto-complete-clang)