-
Notifications
You must be signed in to change notification settings - Fork 55
/
jcomplete.el
273 lines (243 loc) · 9.71 KB
/
jcomplete.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
;;; jdecompletion.el -- Smart completion for the JDEE
;; Author: Rodrigo Reyes <[email protected]>
;; Keywords: java, intellisense, completion
;; Copyright (C) 1999 Rodrigo Reyes
;; Copyright (C) 2009 by Paul Landes
;; This package follows the GNU General Public Licence (GPL), see the
;; COPYING file that comes along with GNU Emacs. This is free software,
;; you can redistribute it and/or modify it under the GNU GPL terms.
;;
;;; Commentary:
;;
;; This package adds smart completion to the JDEE. How it works is
;; simple : put the cursor at the end of a statement "under
;; construction", eg. "myVariable.rem<CURSOR HERE> and call the
;; prf2-complete-at-point emacs-lisp function (this is by default
;; C-.). A completion is then inserted. If multiple completions are
;; possible, calling the completion function again will cycle through
;; all the possibilities (as dabbrev-mode does).
;;
;; TODO :
;;
;; - [EASY] Check for the variables,
;; - [EASY] Check for the static classes
;; - [NOT THAT EASY] Keep the completion information in the minibuffer
;; (it is currently erased after the user presses a key).
;; - [AVERAGE] Add a cache for the class informations.
;;; Code:
(require 'jdee-backend)
(defvar prf2-current-list nil
"The list of all the completion. Each element of the list is a list
which car is the possible completion, and the cdr is an additional
information about this completion.")
(defvar prf2-current-list-index nil
"An index to an element in prf2-current-list. This is used to
cycle the list.")
(defvar prf2-current-beginning (make-marker)
"The beginning of the region where the last completion was inserted.")
(defvar prf2-current-end (make-marker)
"The end of the region where the last completion was inserted.")
(defun prf2-import-list ()
"Build the list of java package declared in the current buffer.
It mostly scans the buffer for 'import' statements, and return the
resulting list. It impliciyly adds the java.lang.* package."
(interactive)
(save-excursion
(goto-char (point-min))
(let (lst first second)
(while (not (null
(re-search-forward "import[ \t\n\r]+\\(\\([a-zA-Z0-9]+[.]\\)+\\)\\([*]\\|[a-zA-Z0-9]+\\)" nil t) ))
(setq first (match-string 1))
(setq second (match-string 3))
(if (string= "*" second)
(setq lst (append lst
(list (list first second))))
(setq lst (append (list (list first second))
lst))))
(if (not (member "java.lang.*" lst))
(setq lst (append lst (list (list "java.lang." "*")))))
lst)))
(defun prf2-valid-java-declaration-at (point varname)
"Verify that a POINT starts a valid java declaration
for the VARNAME variable."
(save-excursion
(goto-char point)
(if (looking-at (concat "\\([A-Za-z_.]+\\)[ \t\n\r]+" varname "[ \t\n\r]*[;=]"))
(match-string 1)
nil)))
(defun prf2-declared-type-of (name)
"Find in the current buffer the java type of the variable NAME. The
function returns a string containing the name of the class, or nil
otherwise. This function does not give the fully-qualified java class
name, it just returns the type as it is declared."
(interactive)
(save-excursion
(let (found res pos orgpt resname)
(while (and (not found)
(search-backward name nil t))
(setq pos (point))
(backward-word 1)
(setq resname (prf2-valid-java-declaration-at (point) name))
(goto-char pos)
(forward-char -1)
(if (not (null resname))
(progn (setq res resname)
(setq found t))))
res)))
(defun prf2-filter-fqn (importlist)
"Filter all the fully-qualified classnames in the import list. It uses
the knowledge that those classnames are at the beginning of the list,
so that it can stops at the first package import (with a star `*' at
the end of the declaration)."
(if (not (null importlist))
(if (string= "*" (car (cdr (car importlist))))
importlist
(prf2-filter-fqn (cdr importlist)))))
(defun prf2-guess-type-of (name)
"Guess the fully qualified name of the class NAME, using the import
list. It returns a string if the fqn was found, or a list of possible
packages otherwise."
(let ((importlist (prf2-import-list)) shortname fullname tmp result)
(while (and (not (null importlist)) (null result))
(setq tmp (car importlist))
(setq shortname (car (cdr tmp)))
(setq fullname (concat (car tmp) name))
(cond
((string= "*" shortname)
(setq result importlist))
((string= name shortname)
(setq result fullname))
(t
(setq importlist (cdr importlist)))))
result))
(defun prf2-get-classinfo (name)
"Return the class info list for the class NAME (possibly the short
java name). This list contains lists of elements, which car is a
possible completion, and the cdr gives additional informations on the
car."
(let ((guessed (prf2-guess-type-of name)) result)
(if (stringp guessed)
(setq result (jdee-backend-get-class-info guessed))
(if (not (null name))
(setq result (jdee-backend-get-class-info-for-import name guessed))))
(if (not (null result))
(eval (read result))
nil)))
(defun prf2-java-variable-at-point ()
"Return the current word, according to java syntax.
A '.' is part of a name."
(interactive)
(save-excursion
(let (start varname curcar found
(original-point (point))
intermediate-point beg-point)
(setq curcar (char-before))
(while (null found)
(cond
((or (and (>= curcar ?a) (<= curcar ?z))
(and (>= curcar ?A) (<= curcar ?Z))
(member curcar '(?_)))
(forward-char -1))
((eq ?. curcar)
(setq found (point)))
(t
(setq found t)))
(setq curcar (char-before)))
;;
(setq intermediate-point (point))
(if (not (eq t found))
(progn
(setq curcar (char-before))
(while (or (and (>= curcar ?a) (<= curcar ?z))
(and (>= curcar ?A) (<= curcar ?Z))
(member curcar '(?. ?_)))
(forward-char -1)
(setq curcar (char-before)))
(setq beg-point (point))
(set-marker prf2-current-beginning intermediate-point)
(set-marker prf2-current-end original-point)
(list (buffer-substring beg-point (- intermediate-point 1))
(buffer-substring intermediate-point original-point)))
nil))))
(defun prf2-build-completion-list (classinfo)
"Build a completion list from the CLASSINFO list, as returned by the
jdee-backend-get-class-info function."
(let ((result nil) (tmp nil))
;; get the variable fields
(setq tmp (car classinfo))
(while (not (null tmp))
(setq result (append (list (list (car tmp))) result))
(setq tmp (cdr tmp)))
;; get the methods
(setq tmp (nth 2 classinfo))
(while (not (null tmp))
(setq result (append (list (list (concat (car (car tmp))"(")
(prf2-build-information-for-completion (car tmp))
;; (car tmp)
)) result))
(setq tmp (cdr tmp)))
result))
(defun prf2-build-information-for-completion (lst)
(let ((result (concat (car (cdr lst)) " " (car lst) "(")))
(setq lst (cdr (cdr lst)))
(while (not (null lst))
(setq result (concat result (car lst)))
(setq lst (cdr lst))
(if (not (null lst))
(setq result (concat result ", "))))
(setq result (concat result ")"))
result))
(defun prf2-complete-cycle ()
"Replace the previous completion by the next one in the list."
(let (elem)
(setq prf2-current-list-index (+ 1 prf2-current-list-index))
(if (>= prf2-current-list-index (length prf2-current-list))
(setq prf2-current-list-index 0))
(setq elem (nth prf2-current-list-index prf2-current-list))
(if (not (null (car elem)))
(progn
(delete-region prf2-current-beginning prf2-current-end)
(insert (car elem))))
(set-marker prf2-current-end (+ (marker-position prf2-current-beginning) (length (car elem))))
(message (car (cdr elem)))
;; (goto-char (marker-position prf2-current-end))
))
(defun prf2-all-completions (pat lst)
(let ((result nil))
(while (not (null lst))
(if (equal 0 (string-match pat (car (car lst))))
(setq result (append (list (car lst)) result)))
(setq lst (cdr lst)))
result))
(defun prf2-complete-at-point ()
"Smart-complete the method at point."
(interactive)
(if (and
(not (null prf2-current-list))
(markerp prf2-current-beginning)
(markerp prf2-current-end)
(marker-position prf2-current-beginning)
(marker-position prf2-current-end)
(>= (point) (marker-position prf2-current-beginning))
(<= (point) (marker-position prf2-current-end))
(eq last-command this-command))
(progn
(prf2-complete-cycle))
(let* ((pair (prf2-java-variable-at-point))
txt classinfo fulllist
)
(if (not (null pair))
(progn
(setq classinfo (prf2-get-classinfo (prf2-declared-type-of (car pair))))
(setq fulllist (prf2-build-completion-list classinfo))
(setq prf2-current-list (prf2-all-completions (car (cdr pair)) fulllist))
(setq prf2-current-list-index -1)
(prf2-complete-cycle))
(progn
(setq prf2-current-list nil)
(message "No completion at this point."))))))
;;
;; A default binding, as exemple.
(global-set-key [(control \.)] 'prf2-complete-at-point)
(provide 'jcomplete)
;;; jcomplete.el ends here