]> code.delx.au - gnu-emacs-elpa/blobdiff - company-clang.el
Separate executable from arguments in the error output buffer
[gnu-emacs-elpa] / company-clang.el
index e239c615de48c2dc595d4707f2abda614dc89e3b..6d1737a7f1ce3ae06a1d1c32f555efaf4b6f34c4 100644 (file)
@@ -1,4 +1,4 @@
-;;; 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, 2013  Free Software Foundation, Inc.
 
 ;;; 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
   :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
+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
   :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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -99,15 +102,22 @@ 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*")
 
 (defvar company-clang--meta-cache nil)
 
-(defun company-clang--parse-output (prefix)
+(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)))
@@ -116,22 +126,31 @@ Prefix files (-include ...) can be selected with
     (setq company-clang--meta-cache (make-hash-table :test 'equal))
     (while (re-search-forward pattern nil t)
       (setq match (match-string-no-properties 1))
-      (let ((meta (match-string-no-properties 2)))
-        (when (and meta (not (string= match meta)))
-          (puthash match meta company-clang--meta-cache)))
       (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))))
 
@@ -146,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" "-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)
@@ -179,10 +209,26 @@ 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)
 
+(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
@@ -196,52 +242,56 @@ Prefix files (-include ...) can be selected with
       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)) "<arg>"))
-      (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 (< (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))
-    (meta (let ((meta (gethash arg company-clang--meta-cache)))
-            (when meta
-              (replace-regexp-in-string
-               "#]" " "
-               (replace-regexp-in-string "[<{[]#\\|#[>}]" "" meta t)
-               t))))
-    (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