X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/1acf9ec859d39ee5c1a04cbb4d4bcf51fcb41788..a5433b904ffc3b1cb51cc108e24a9d6bd6ca6bc6:/packages/company/company-clang.el diff --git a/packages/company/company-clang.el b/packages/company/company-clang.el index 51eb78fbd..6d1737a7f 100644 --- a/packages/company/company-clang.el +++ b/packages/company/company-clang.el @@ -1,6 +1,6 @@ -;;; company-clang.el --- A company-mode completion back-end for clang +;;; company-clang.el --- company-mode completion back-end for Clang -;; Copyright (C) 2009, 2011 Free Software Foundation, Inc. +;; Copyright (C) 2009, 2011, 2013 Free Software Foundation, Inc. ;; Author: Nikolaj Schumacher @@ -21,40 +21,43 @@ ;;; Commentary: -;; +;; ;;; Code: (require 'company) +(require 'company-template) (eval-when-compile (require 'cl)) +(defgroup company-clang nil + "Completion back-end for Clang." + :group 'company) + (defcustom company-clang-executable (executable-find "clang") - "*Location of clang executable." - :group 'company-clang + "Location of clang executable." :type 'file) -(defcustom company-clang-auto-save t - "*Determines whether to save the buffer when retrieving completions. -clang can only complete correctly when the buffer has been saved." - :group 'company-clang - :type '(choice (const :tag "Off" nil) - (const :tag "On" t))) +(defcustom company-clang-begin-after-member-access t + "When non-nil, automatic completion will start whenever the current +symbol is preceded by \".\", \"->\" or \"::\", ignoring +`company-minimum-prefix-length'. + +If `company-begin-commands' is a list, it should include `c-electric-lt-gt' +and `c-electric-colon', for automatic completion right after \">\" and +\":\".") (defcustom company-clang-arguments nil - "*Additional arguments to pass to clang when completing. -Prefix files (-include ...) can be selected with -`company-clang-set-prefix' or automatically through a custom -`company-clang-prefix-guesser'." - :group 'company-clang + "Additional arguments to pass to clang when completing. +Prefix files (-include ...) can be selected with `company-clang-set-prefix' +or automatically through a custom `company-clang-prefix-guesser'." :type '(repeat (string :tag "Argument" nil))) (defcustom company-clang-prefix-guesser 'company-clang-guess-prefix - "*A function to determine the prefix file for the current buffer." - :group 'company-clang + "A function to determine the prefix file for the current buffer." :type '(function :tag "Guesser function" nil)) -(defvar company-clang-modes '(c-mode objc-mode) +(defvar company-clang-modes '(c-mode c++-mode objc-mode) "Major modes which clang may complete.") ;; prefix ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -77,7 +80,7 @@ Prefix files (-include ...) can be selected with ;; Prefixes seem to be called .pch. Pre-compiled headers do, too. ;; So we look at the magic number to rule them out. (let* ((file (company-clang--guess-pch-file buffer-file-name)) - (magic-number (company-clang--file-substring file 0 4))) + (magic-number (and file (company-clang--file-substring file 0 4)))) (unless (member magic-number '("CPCH" "gpch")) file))) @@ -99,32 +102,55 @@ Prefix files (-include ...) can be selected with ;; parsing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; TODO: How to handle OVERLOAD and Pattern? +;; TODO: Handle Pattern (syntactic hints would be neat). +;; Do we ever see OVERLOAD (or OVERRIDE)? (defconst company-clang--completion-pattern - "^COMPLETION: \\_<\\(%s[a-zA-Z0-9_:]*\\)") + "^COMPLETION: \\_<\\(%s[a-zA-Z0-9_:]*\\)\\(?: : \\(.*\\)$\\)?$") (defconst company-clang--error-buffer-name "*clang error*") -(defun company-clang--parse-output (prefix) +(defvar company-clang--meta-cache nil) + +(defun company-clang--lang-option () + (if (eq major-mode 'objc-mode) + (if (string= "m" (file-name-extension buffer-file-name)) + "objective-c" "objective-c++") + (substring (symbol-name major-mode) 0 -5))) + +(defun company-clang--parse-output (prefix objc) (goto-char (point-min)) (let ((pattern (format company-clang--completion-pattern (regexp-quote prefix))) + (case-fold-search nil) lines match) + (setq company-clang--meta-cache (make-hash-table :test 'equal)) (while (re-search-forward pattern nil t) (setq match (match-string-no-properties 1)) (unless (equal match "Pattern") + (let ((meta (match-string-no-properties 2))) + (when (and meta (not (string= match meta))) + (setq meta (company-clang--strip-formatting meta)) + (when (and (not objc) (string-match "\\((.*)\\)" meta)) + (setq match (concat match (match-string 1 meta)))) + (puthash match meta company-clang--meta-cache))) (push match lines))) lines)) +(defun company-clang--strip-formatting (text) + (replace-regexp-in-string + "#]" " " + (replace-regexp-in-string "[<{[]#\\|#[>}]" "" text t) + t)) + (defun company-clang--handle-error (res args) (goto-char (point-min)) (let* ((buf (get-buffer-create company-clang--error-buffer-name)) - (cmd (concat company-clang-executable (mapconcat 'identity args " "))) + (cmd (concat company-clang-executable " " (mapconcat 'identity args " "))) (pattern (format company-clang--completion-pattern "")) (err (if (re-search-forward pattern nil t) (buffer-substring-no-properties (point-min) (1- (match-beginning 0))) - ;; Warn the user more agressively if no match was found. + ;; Warn the user more aggressively if no match was found. (message "clang failed with error %d:\n%s" res cmd) (buffer-string)))) @@ -139,30 +165,41 @@ Prefix files (-include ...) can be selected with (goto-char (point-min)))))) (defun company-clang--call-process (prefix &rest args) - (with-temp-buffer - (let ((res (apply 'call-process company-clang-executable nil t nil args))) + (let ((objc (derived-mode-p 'objc-mode)) + (buf (get-buffer-create "*clang-output*")) + res) + (with-current-buffer buf (erase-buffer)) + (setq res (if (company-clang--auto-save-p) + (apply 'call-process company-clang-executable nil buf nil args) + (apply 'call-process-region (point-min) (point-max) + company-clang-executable nil buf nil args))) + (with-current-buffer buf (unless (eq 0 res) (company-clang--handle-error res args)) ;; Still try to get any useful input. - (company-clang--parse-output prefix)))) + (company-clang--parse-output prefix objc)))) (defsubst company-clang--build-location (pos) (save-excursion (goto-char pos) - (format "%s:%d:%d" buffer-file-name (line-number-at-pos) + (format "%s:%d:%d" + (if (company-clang--auto-save-p) buffer-file-name "-") + (line-number-at-pos) (1+ (current-column))))) (defsubst company-clang--build-complete-args (pos) - (append '("-cc1" "-fsyntax-only") + (append '("-cc1" "-fsyntax-only" "-code-completion-macros") + (unless (company-clang--auto-save-p) + (list "-x" (company-clang--lang-option))) company-clang-arguments (when (stringp company-clang--prefix) (list "-include" (expand-file-name company-clang--prefix))) '("-code-completion-at") (list (company-clang--build-location pos)) - (list buffer-file-name))) + (list (if (company-clang--auto-save-p) buffer-file-name "-")))) (defun company-clang--candidates (prefix) - (and company-clang-auto-save + (and (company-clang--auto-save-p) (buffer-modified-p) (basic-save-buffer)) (when (null company-clang--prefix) @@ -172,60 +209,89 @@ Prefix files (-include ...) can be selected with prefix (company-clang--build-complete-args (- (point) (length prefix))))) +(defun company-clang--prefix () + (let ((symbol (company-grab-symbol))) + (if symbol + (if (and company-clang-begin-after-member-access + (save-excursion + (forward-char (- (length symbol))) + (looking-back "\\.\\|->\\|::" (- (point) 2)))) + (cons symbol t) + symbol) + 'stop))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defconst company-clang-required-version "1.1") +(defconst company-clang-required-version 1.1) + +(defvar company-clang--version nil) + +(defun company-clang--auto-save-p () + (< company-clang--version 2.9)) (defsubst company-clang-version () "Return the version of `company-clang-executable'." (with-temp-buffer (call-process company-clang-executable nil t nil "--version") (goto-char (point-min)) - (when (re-search-forward "\\`clang version \\([0-9.]+\\)" nil t) - (match-string-no-properties 1)))) + (if (re-search-forward "clang\\(?: version \\|-\\)\\([0-9.]+\\)" nil t) + (let ((ver (string-to-number (match-string-no-properties 1)))) + (if (> ver 100) + (/ ver 100) + ver)) + 0))) (defun company-clang-objc-templatify (selector) - (let* ((end (point)) + (let* ((end (point-marker)) (beg (- (point) (length selector))) - (templ (company-template-declare-template beg end))) + (templ (company-template-declare-template beg end)) + (cnt 0)) (save-excursion (goto-char beg) - (while (search-forward ":" end t) - (replace-match ": ") - (incf end 2) - (company-template-add-field templ (1- (match-end 0)) "")) - (delete-char -1)) + (catch 'stop + (while (search-forward ":" end t) + (company-template-add-field templ (point) (format "arg%d" cnt)) + (if (< (point) end) + (insert " ") + (throw 'stop t)) + (incf cnt)))) (company-template-move-to-first templ))) (defun company-clang (command &optional arg &rest ignored) - "A `company-mode' completion back-end for clang. -Clang is a parser for C and ObjC. The unreleased development version of -clang (1.1) is required. + "`company-mode' completion back-end for Clang. +Clang is a parser for C and ObjC. Clang version 1.1 or newer is required. Additional command line arguments can be specified in `company-clang-arguments'. Prefix files (-include ...) can be selected with `company-clang-set-prefix' or automatically through a custom `company-clang-prefix-guesser'. -Completions only work correctly when the buffer has been saved. -`company-clang-auto-save' determines whether to do this automatically." +With Clang versions before 2.9, we have to save the buffer before +performing completion. With Clang 2.9 and later, buffer contents are +passed via standard input." (interactive (list 'interactive)) (case command (interactive (company-begin-backend 'company-clang)) - (init (unless company-clang-executable - (error "Company found no clang executable")) - (when (version< (company-clang-version) - company-clang-required-version) - (error "Company requires clang version 1.1"))) + (init (when (memq major-mode company-clang-modes) + (unless company-clang-executable + (error "Company found no clang executable")) + (setq company-clang--version (company-clang-version)) + (when (< company-clang--version company-clang-required-version) + (error "Company requires clang version 1.1")))) (prefix (and (memq major-mode company-clang-modes) buffer-file-name company-clang-executable (not (company-in-string-or-comment)) - (or (company-grab-symbol) 'stop))) + (company-clang--prefix))) (candidates (company-clang--candidates arg)) - (post-completion (and (derived-mode-p 'objc-mode) - (string-match ":" arg) - (company-clang-objc-templatify arg))))) + (meta (gethash arg company-clang--meta-cache)) + (crop (and (string-match ":\\|(" arg) + (substring arg 0 (match-beginning 0)))) + (post-completion (cond + ((not (derived-mode-p 'objc-mode)) + (company-template-c-like-templatify arg)) + ((string-match ":" arg) + (company-clang-objc-templatify arg)))))) (provide 'company-clang) ;;; company-clang.el ends here