-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathmagik-cb-ac.el
executable file
·268 lines (248 loc) · 11.3 KB
/
magik-cb-ac.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
;;; magik-cb-ac.el --- Magik Classbrowser Autocomplete Support
;; 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 3 of the License, 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
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;;; Code:
(require 'auto-complete)
;; A U T O - C O M P L E T E
;; _________________________
(defun magik-cb-ac-filter (p s)
"Process data coming back from the CB auto-complete buffer (P).
Using S as the filter string."
(with-current-buffer (process-buffer p)
(unwind-protect
(let ((buffer-read-only nil)
(coding-system-for-read magik-cb-coding-system)
fn)
(setq magik-cb-filter-str (concat magik-cb-filter-str s))
(save-match-data
(setq fn (cond ((string-match "\C-e" magik-cb-filter-str)
'magik-cb-ac-candidate-methods)
((string-match "\C-c" magik-cb-filter-str)
'magik-cb-ac-candidate-classes)
(t
nil))))
(setq magik-cb-filter-str ""
magik-cb--ac-candidates (if fn
(progn
(insert-file-contents (magik-cb-temp-file-name p) nil nil nil t)
(funcall fn)))))
(setq magik-cb-filter-str ""
magik-cb--ac-candidates (if (eq magik-cb--ac-candidates 'unset) nil magik-cb--ac-candidates)))))
(defun magik-cb-ac-start-process ()
"Start a Class Browser process for auto-complete-mode.
Stores process object in `magik-cb-ac-process'."
; TODO get-gis-buffer
(setq magik-cb-ac-process (magik-cb-get-process-create "*cb-ac*" 'magik-cb-ac-filter "*gis*" nil)))
(defun magik-cb-ac-candidate-methods ()
"Return candidate methods matching `ac-prefix' from Method finder output."
;;TODO combine method definition with its signature.
(let ((method (car ac-prefix))
(class (cdr ac-prefix))
(ac-limit ac-limit))
(setq method
(if (zerop (length method))
"\\sw"
(regexp-quote method)))
(let ((i 0)
(regexp (concat "^\\(" method "\\S-*\\)" magik-cb-in-keyword "\\(\\S-+\\)\\s-+\\(.*\\)\n\\(.*\n\\)\n\\(\\( +##.*\n\\)*\\)")) ; capture item and comments
candidate
classify
args
documentation
candidates)
(goto-char (point-min))
(save-match-data
(while (and (or (null ac-limit) (< i ac-limit))
(re-search-forward regexp nil t))
(setq candidate (match-string-no-properties 1)
class (match-string-no-properties 2)
classify (match-string-no-properties 3)
args (magik-cb-method-args (match-beginning 4))
documentation (match-string-no-properties 5))
(put-text-property 0 (length candidate)
'document
(magik-cb-method-docstring class candidate args classify documentation)
candidate)
(if (member candidate candidates)
nil ; already present
(setq candidates (append (list candidate) candidates)
i (1+ i)))))
(nreverse candidates))))
(defun magik-cb-method-args (pt)
"Return method arguments from Class Browser at point PT."
(save-excursion
(goto-char pt)
(save-match-data
(let ((case-fold-search nil)
optional
args
gather
opt
name)
(if (looking-at "$")
nil ; No arguments
(forward-char 1) ; space
(while (not (looking-at "$"))
(setq pt (point))
(cond ((looking-at "\\(OPT \\)?GATH \\(.*\\)")
(setq gather (buffer-substring-no-properties (match-beginning 2) (match-end 2)))
(goto-char (match-end 0)))
((looking-at "OPT ")
(setq opt t)
(goto-char (match-end 0)))
((> (skip-syntax-forward "w_") 0) ; found argument (may contain _ which may be classed as symbols.)
(setq name (list (buffer-substring-no-properties pt (point))))
(if opt
(setq optional (append optional name))
(setq args (append args name)))
(if (eq (following-char) ? )
(forward-char 1)))
(t ;catch all error
(message "Found unrecognised character at %d in %s" (point) (current-buffer))
(goto-char (end-of-line))))))
(list args optional gather)))))
;;TODO extract out Magik method signature from classification and documentation processing.
(defun magik-cb-method-docstring (class candidate args classify documentation)
"Return method documentation string."
(let* ((required (elt args 0))
(optional (elt args 1))
(gather (elt args 2))
(method-signature (magik-method-name-type candidate))
(method (car method-signature))
(signature (cdr method-signature))
(signature-p (> (length signature) 0))
assignment)
;;Standardise classification string
(cond ((zerop (length classify))
;;do nothing
nil)
((equal (substring classify 0 1) "A")
(setq classify (concat "Advanced" (substring classify 1))))
((equal (substring classify 0 1) "B")
(setq classify (concat "Basic" (substring classify 1))))
(t
;;do nothing
nil))
;; Handle << assignment like signatures - take first required argument
(if (and signature-p (equal (substring signature -1) "<"))
(setq assignment (car required)
required (cdr required)))
(if documentation
(while (string-match "^ +## " documentation)
(setq documentation (replace-match "" nil nil documentation))))
(if gather
;; prefix rest args with _gather and convert to a list.
(setq gather (list (concat "_gather " gather))))
(if optional
;; prefix first optional arg with _optional
(setcar optional (concat "_optional " (car optional))))
;; TODO handle arrays [], []<< etc.
(concat
(cond ((equal class "<condition>")
(concat "raise(:" method (if required ",\n ")
(mapconcat (lambda (r) (concat ":" r ", <value>")) required ",\n ")
")\n"
" " classify
"\n"))
((equal class "<global>")
;; Globals are either procedures with arguments or dynamics.
(let* ((args-string (mapconcat 'identity (append required optional gather) ", "))
(argsp (not (equal args-string ""))))
(concat method
(if argsp "(")
args-string
(if argsp ")")
"\n"
" " classify
"\n")))
((not signature-p)
(concat method
"\n"
" " classify
"\n"))
((equal (substring signature 0 1) "(")
(let ((args-string (mapconcat 'identity (append required optional gather) ", ")))
(concat method "("
args-string
(substring signature 1) ;; appends rest of signature. ), )<< and )^<<
assignment ;; allows for ()<< and ()^<< too.
"\n"
" " classify
"\n")))
(assignment ;; handle << and ^<<
(concat method
signature
assignment
"\n"
" " classify
"\n"))
(t "UNKNOWN??\n\n"))
documentation)))
(defun magik-cb-ac-candidate-classes ()
"Return candidate classes from Method finder output."
;TODO handle package definitions?
(let ((i 0)
(regexp (concat "\\(\\S-+:\\)\\(\\S-+\\)")) ; capture class name and its package
candidate
candidates)
(goto-char (point-min))
(save-match-data
(while (re-search-forward regexp nil t)
(setq candidate (match-string-no-properties 2))
(save-match-data
(if (member candidate candidates)
nil ; already present
(setq candidates (append (list candidate) candidates)
i (1+ i)))))
(nreverse candidates))))
(defun magik-cb-ac-method-candidates ()
"Return list of methods for a class matching AC-PREFIX for auto-complete mode.
AC-PREFIX is of the form \"CLASS\".\"METHOD_NAME_PREFIX\""
(let ((magik-cb--ac-candidates 'unset) ; use 'unset symbol since nil is also a valid return value.
(ac-prefix ac-prefix)
(ac-limit (or ac-limit 1000000))
class method character)
(save-match-data
(cond ((null magik-cb-ac-process)
(setq magik-cb--ac-candidates nil))
((not (string-match "\\(\\S-+\\)\\.\\(.*\\)" ac-prefix))
(setq magik-cb--ac-candidates nil))
(t
(setq class (match-string-no-properties 1 ac-prefix)
method (match-string-no-properties 2 ac-prefix)
character (if (equal method "") method (substring method 0 1))
ac-prefix (cons method class))
(process-send-string magik-cb-ac-process
(concat "method_name ^" character "\n"
"unadd class \nadd class " class "\n"
"method_cut_off " (number-to-string ac-limit) "\n"
"override_flags\nshow_classes\nshow_args\nshow_comments\nprint_curr_methods\n"))
(while (and (eq magik-cb--ac-candidates 'unset)
(magik-cb-is-running nil magik-cb-ac-process))
(sleep-for 0.1))
(setq magik-cb--ac-candidates (append (list (concat " " class "." character)) magik-cb--ac-candidates)))))
magik-cb--ac-candidates))
(defun magik-cb-ac-class-candidates ()
"Return list of classes matching AC-PREFIX for auto-complete mode."
(let ((magik-cb--ac-candidates 'unset)) ; use 'unset symbol since nil is also a valid return value.
(cond ((null magik-cb-ac-process)
(setq magik-cb--ac-candidates nil))
(t
(process-send-string magik-cb-ac-process
(concat "dont_override_flags\npr_family " ac-prefix "\n"))
(while (and (eq magik-cb--ac-candidates 'unset)
(magik-cb-is-running nil magik-cb-ac-process))
(sleep-for 0.1))))
magik-cb--ac-candidates))
(provide 'magik-cb-ac)
;;; magik-cb-ac.el ends here