-;;; company-eclim.el --- A company-mode completion back-end for eclim.
+;;; company-eclim.el --- company-mode completion backend for Eclim
-;; Copyright (C) 2009, 2011, 2013 Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2011, 2013, 2015 Free Software Foundation, Inc.
;; Author: Nikolaj Schumacher
;;; Commentary:
;;
-;; Eclim version 1.7.13 or newer (?) is required.
+;; Using `emacs-eclim' together with (or instead of) this backend is
+;; recommended, as it allows you to use other Eclim features.
;;
-;; This completion backend is pretty barebone.
-;;
-;; `emacs-eclim' provides an alternative backend, and it also allows you to
-;; actually control Eclim from Emacs.
+;; The alternative backend provided by `emacs-eclim' uses `yasnippet'
+;; instead of `company-template' to expand function calls, and it supports
+;; some languages other than Java.
;;; Code:
(require 'company)
-(eval-when-compile (require 'cl))
+(require 'company-template)
+(require 'cl-lib)
+
+(defgroup company-eclim nil
+ "Completion backend for Eclim."
+ :group 'company)
(defun company-eclim-executable-find ()
(let (file)
- (dolist (eclipse-root '("/Applications/eclipse" "/usr/lib/eclipse"
+ (cl-dolist (eclipse-root '("/Applications/eclipse" "/usr/lib/eclipse"
"/usr/local/lib/eclipse"))
(and (file-exists-p (setq file (expand-file-name "plugins" eclipse-root)))
(setq file (car (last (directory-files file t "^org.eclim_"))))
(file-exists-p (setq file (expand-file-name "bin/eclim" file)))
- (return file)))))
+ (cl-return file)))))
(defcustom company-eclim-executable
- (or (executable-find "eclim") (company-eclim-executable-find))
+ (or (bound-and-true-p eclim-executable)
+ (executable-find "eclim")
+ (company-eclim-executable-find))
"Location of eclim executable."
- :group 'company
:type 'file)
(defcustom company-eclim-auto-save t
"Determines whether to save the buffer when retrieving completions.
eclim can only complete correctly when the buffer has been saved."
- :group 'company
:type '(choice (const :tag "Off" nil)
(const :tag "On" t)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(defvar company-eclim--project-dir 'unknown)
-(make-variable-buffer-local 'company-eclim--project-dir)
+(defvar-local company-eclim--project-dir 'unknown)
-(defvar company-eclim--project-name 'unknown)
-(make-variable-buffer-local 'company-eclim--project-name)
+(defvar-local company-eclim--project-name nil)
-(defvar company-eclim--doc nil)
-(make-variable-buffer-local 'company-eclim--doc)
+(declare-function json-read "json")
+(defvar json-array-type)
(defun company-eclim--call-process (&rest args)
(let ((coding-system-for-read 'utf-8)
(setq company-eclim--project-dir
(directory-file-name
(expand-file-name
- (company-locate-dominating-file buffer-file-name ".project"))))
+ (locate-dominating-file buffer-file-name ".project"))))
company-eclim--project-dir))
(defun company-eclim--project-name ()
- (if (eq company-eclim--project-name 'unknown)
- (setq company-eclim--project-name
- (let ((project (find-if (lambda (project)
- (equal (cdr (assoc 'path project))
- (company-eclim--project-dir)))
- (company-eclim--project-list))))
- (when project
- (cdr (assoc 'name project)))))
- company-eclim--project-name))
+ (or company-eclim--project-name
+ (let ((dir (company-eclim--project-dir)))
+ (when dir
+ (setq company-eclim--project-name
+ (cl-loop for project in (company-eclim--project-list)
+ when (equal (cdr (assoc 'path project)) dir)
+ return (cdr (assoc 'name project))))))))
(defun company-eclim--candidates (prefix)
(interactive "d")
(let ((project-file (file-relative-name buffer-file-name
(company-eclim--project-dir)))
- (project-name (company-eclim--project-name)))
+ completions)
(when company-eclim-auto-save
(when (buffer-modified-p)
(basic-save-buffer))
(company-eclim--call-process "java_src_update"
"-p" (company-eclim--project-name)
"-f" project-file))
- (setq company-eclim--doc
- (cdr (assoc 'completions
- (company-eclim--call-process
- "java_complete" "-p" (company-eclim--project-name)
- "-f" project-file
- "-o" (number-to-string (1- (point)))
- "-e" "utf-8"
- "-l" "standard")))))
- (let ((completion-ignore-case nil))
- ;; TODO: Handle overloaded methods somehow. Show one candidate per overload?
- ;; That would look nice, but kinda useless: a bunch of candidates for the
- ;; same completion. Maybe do expansion like `company-clang-objc-templatify'.
- (all-completions prefix (mapcar (lambda (item) (cdr (assoc 'completion item)))
- company-eclim--doc))))
+ (dolist (item (cdr (assoc 'completions
+ (company-eclim--call-process
+ "java_complete" "-p" (company-eclim--project-name)
+ "-f" project-file
+ "-o" (number-to-string
+ (company-eclim--search-point prefix))
+ "-e" "utf-8"
+ "-l" "standard"))))
+ (let* ((meta (cdr (assoc 'info item)))
+ (completion meta))
+ (when (string-match " ?[(:-]" completion)
+ (setq completion (substring completion 0 (match-beginning 0))))
+ (put-text-property 0 1 'meta meta completion)
+ (push completion completions)))
+ (let ((completion-ignore-case nil))
+ (all-completions prefix completions))))
+
+(defun company-eclim--search-point (prefix)
+ (if (or (cl-plusp (length prefix)) (eq (char-before) ?.))
+ (1- (point))
+ (point)))
(defun company-eclim--meta (candidate)
- (cdr (assoc 'info (find-if
- (lambda (item) (equal (cdr (assoc 'completion item))
- arg))
- company-eclim--doc))))
+ (get-text-property 0 'meta candidate))
+
+(defun company-eclim--annotation (candidate)
+ (let ((meta (company-eclim--meta candidate)))
+ (when (string-match "\\(([^-]*\\) -" meta)
+ (substring meta (match-beginning 1) (match-end 1)))))
+
+(defun company-eclim--prefix ()
+ (let ((prefix (company-grab-symbol)))
+ (when prefix
+ ;; Completion candidates for annotations don't include '@'.
+ (when (eq ?@ (string-to-char prefix))
+ (setq prefix (substring prefix 1)))
+ prefix)))
(defun company-eclim (command &optional arg &rest ignored)
- "A `company-mode' completion back-end for eclim.
-eclim provides access to Eclipse Java IDE features for other editors.
+ "`company-mode' completion backend for Eclim.
+Eclim provides access to Eclipse Java IDE features for other editors.
+
+Eclim version 1.7.13 or newer (?) is required.
Completions only work correctly when the buffer has been saved.
`company-eclim-auto-save' determines whether to do this automatically."
(interactive (list 'interactive))
- (case command
+ (cl-case command
(interactive (company-begin-backend 'company-eclim))
(prefix (and (derived-mode-p 'java-mode 'jde-mode)
buffer-file-name
company-eclim-executable
(company-eclim--project-name)
(not (company-in-string-or-comment))
- (or (company-grab-symbol) 'stop)))
+ (or (company-eclim--prefix) 'stop)))
(candidates (company-eclim--candidates arg))
(meta (company-eclim--meta arg))
- (duplicates t)
;; because "" doesn't return everything
- (no-cache (equal arg ""))))
+ (no-cache (equal arg ""))
+ (annotation (company-eclim--annotation arg))
+ (post-completion (let ((anno (company-eclim--annotation arg)))
+ (when anno
+ (insert anno)
+ (company-template-c-like-templatify anno))))))
(provide 'company-eclim)
;;; company-eclim.el ends here