[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


From: Nic Ferrier
Subject: java-complete.el
Date: 07 Nov 2004 00:14:43 +0000
User-agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.3.50

A new java-completion system. Source and some description is available

But because this is a source forum, here is the elisp:

;; Java completion.
;; (C) Tapsell-Ferrier Limited 2004

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;; I have always wanted 3 sorts of completion in Java:
;; 1. complete a class name;  for when you're declaring variables
;; 2. complete a method call; given text:
;;     someVar.meth
;;    - find the type of someVar
;;    - complete meth given the list of possible methods
;; 3. complete package names in import statements
;; When I'm completing I'd like to see nice status about what has been 

(defvar java-complete-class-p 't
  "A switch that controls the completer.
If it's true then the completer does class completion, if false
it does member completion.")

(defvar java-complete-package-re ""
  "A regex that matches packages prefixs.
This is used to search across everything that a file imports.")

(defun java-complete ()
    (if (looking-back "\\.[A-Za-z0-9]*" (line-beginning-position))

(defun java-complete-class ()
  (let ((sym (progn
               (re-search-backward "[ \t]\\([A-Za-z0-9_]*\\)" 
(line-beginning-position) 't)
               (match-string 1))))
      (let* ((java-complete-package-re (java-complete-import-re)) ; 
java-complete-package-re is used dynamically
             (completed (try-completion sym 'java-completer)))
         ((eq completed 't)
          (message "already complete"))
         ((eq completed nil)
          (message "no completion"))

         ((stringp completed) ; it's a completion
          (if (equal completed sym)
              (with-output-to-temp-buffer "*Completions*"
                 (all-completions completed 'java-completer)))
            ;; Else the completion did cause an expansion, so put it in.
              (replace-match completed 't 't nil 1)
              (indent-region (line-beginning-position) 

(defun java-complete-member ()
  ;; This func is only called when point is preceeded by a "xxx." pattern
  ;; So we already know there's a variable to be found
  (let* (java-complete-class-name
         (pt (point))
         (sym (progn
                (re-search-backward "\\.\\([A-Za-z0-9]*\\)" 
(line-beginning-position) 't)
                (match-string 1))))
    ;; Find some other bits but protect the last re stats
        (re-search-backward " \\([a-zA-Z0-9]+\\)" (line-beginning-position) 't)
        (setq variable (match-string 1))
        ;; Finding the variable's decl is tricky.
        ;; We should really use C mode's syntactic marking to find the right 
        ;; but you have to try to make a sensible decision about where the decl 
        ;; It could be in the current scope, in which case we could search 
        ;; (but we might hit previous, deeper scopes)
        ;; If the decl is in clas scope it could be anywhere in relation to 
        ;; So this will probably have to learn some scoping rules.
        (if (not (re-search-backward (concat "[ \t]\\([A-Za-z0-9.]+\\)[ ]*" 
variable "[ ]*=[ ]*") nil 't))
            (if (not (re-search-backward (concat "[ \t]\\([A-Za-z0-9.]+\\)[ ]*" 
variable "[ ]*;") nil 't))
                (if (not (re-search-backward (concat "([ 
\t,]*\\([A-Za-z0-9.]+\\)[ ]*" variable "[ ]*[,)]") nil 't))
                    (error (concat "No decl found for " sym)))))
        (setq java-complete-class-name (match-string 1))))
    ;; Now call the completer
    (let* ((java-complete-package-re (java-complete-import-re))
           (java-complete-class-p nil)
           (completed (try-completion sym 'java-completer)))
       ((stringp completed)
        (if (equal completed sym)
            (with-output-to-temp-buffer "*Completions*"
               (all-completions completed 'java-completer)))
          ;; Else the completion did cause an expansion, so put it in.
            (replace-match completed 't 't nil 1)
            (indent-region (line-beginning-position) (line-end-position)))))))))

(defun java-complete-import-re ()
  "make a regex to match the packages in the import statements"
  (let ((regex "java\\.lang\\."))
        (let ((class-start (save-excursion
                             (re-search-forward "^.* \\(class \\|interface \\)" 
nil 't))))
          (if (not class-start)
              (error "this not a java class or interface"))
          (while (re-search-forward "^import \\([a-z0-9.]+\\).*;" class-start 
            (setq regex (concat regex "\\|" (regexp-quote (match-string 1))))
      (concat "\\(" regex "\\)")

(defun java-complete-tags-finder (buffer &optional directory)
  "Find a java.tags file for the specified buffer.
The buffer might have the variable java-completer-tag-buffer set.
If so then the value of that is used. Otherwise the tag file is
seacrhed for from the current directory towards the root.  If a
tag file still isn't found then a user specific tags file is
tried: ~/.java.tags
If that isn't found either then an error is signalled."
  (defun mkfilename (dir name)
    (file-truename (expand-file-name (concat dir name))))
  ;; Main func.
  (let* ((dir1 (or directory (file-name-directory (buffer-file-name buffer))))
         (dirlst (directory-files dir1)))
    ;; Visit any file found
    (if (and (member ".java.tags" dirlst)
             (file-exists-p (mkfilename dir1 ".java.tags")))
        (let ((buf (find-file-noselect (mkfilename dir1 ".java.tags"))))
          (make-local-variable 'java-complete-tags)
          (setq java-complete-tags buf))
      (let ((parentdir (mkfilename dir1 "../")))
        (if (not (equal "/" parentdir))
            (java-complete-tags-finder buffer parentdir)
          (if (file-exists-p "~/.java.tags")
              (find-file-noselect "~/.java.tags")
            (error "could not find a java tags file, consider making a user 
specific one?")))))))

(defun java-completer (string predicate all-completions)
  "The completer.
This does most of the completion work scanning the buffer java.tags."
      (with-current-buffer (java-complete-tags-finder (current-buffer))
        (let ((case-fold-search nil)
              (result (list '()))
              (re (if java-complete-class-p
                      (concat "^" java-complete-package-re "\\(" (regexp-quote 
string) "[A-Za-z0-9_]*\\)$")
                    ;; Else it's member completion
                    (concat "^[ \t]*\\(public \\|protected \\|abstract 
\\|static \\|final \\)"
                            java-complete-class-name "\\." ; the "." here 
ensures we catch only members (not constructors)
                            "\\(" (regexp-quote string) 
          (while (re-search-forward re nil 't)
            (nconc result (list (cons (match-string (if java-complete-class-p 2 
                                      (match-string 0))))
          ;; What we do with the result depends on whether we were called as
          ;; try-completion or as all-completions
          (if all-completions
              ;; FIXME::: returning the list directly should work!!
              ;; seems to be a bug in display-completion-list that causes it to 
              (mapcar (lambda (pair)
                        (car pair))  (cdr result))
            (try-completion string (cdr result) predicate)))))))

(provide 'java-complete)

;; End.

Nic Ferrier

reply via email to

[Prev in Thread] Current Thread [Next in Thread]