comparison lisp/oobr/br-eif-ft.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 4103f0995bd7
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;!emacs
2 ;;
3 ;; FILE: br-eif-ft.el
4 ;; SUMMARY: Eiffel OO-Browser class and feature functions.
5 ;; USAGE: GNU Emacs Lisp Library
6 ;; KEYWORDS: oop, tools
7 ;;
8 ;; AUTHOR: Bob Weiner
9 ;; ORG: Motorola Inc.
10 ;;
11 ;; ORIG-DATE: 03-Oct-90
12 ;; LAST-MOD: 11-May-95 at 11:24:33 by Bob Weiner
13 ;;
14 ;; Copyright (C) 1990-1995 Free Software Foundation, Inc.
15 ;; See the file BR-COPY for license information.
16 ;;
17 ;; This file is part of the OO-Browser.
18 ;;
19 ;; DESCRIPTION:
20 ;; DESCRIP-END.
21
22 ;; ************************************************************************
23 ;; Other required Elisp libraries
24 ;; ************************************************************************
25
26 (require 'eif-calls)
27
28 ;; ************************************************************************
29 ;; Public variables
30 ;; ************************************************************************
31
32 (defconst eif-type-tag-separator ","
33 "String that separates a tags type from its normalized definition form.")
34
35 ;; ************************************************************************
36 ;; Public functions
37 ;; ************************************************************************
38
39 (defun eif-feature-implementors (ftr-name)
40 "Return unsorted list of Eiffel feature tags which implement FTR-NAME."
41 (eif-feature-matches (concat "^" (regexp-quote ftr-name) "$")))
42
43 (defun eif-feature-name-to-regexp (name)
44 "Converts feature NAME into a regular expression matching the feature's name tag."
45 (if (string-match (concat "^" br-feature-type-regexp " ") name)
46 (setq name (substring name (match-end 0))))
47 (format "%s%s%s %s[ \n]"
48 eif-identifier eif-type-tag-separator br-feature-type-regexp
49 (regexp-quote name)))
50
51 (fset 'eif-feature-signature-to-name 'eif-feature-partial-name)
52
53 (defun eif-feature-signature-to-regexp (signature)
54 "Given an Eiffel class or feature SIGNATURE, return regexp to match its definition."
55 (let ((regexp) class name type)
56 (setq regexp
57 (cond ((string-match (concat eif-type-tag-separator
58 "\\(" br-feature-type-regexp "\\) ")
59 signature)
60 (setq name (substring signature (match-end 0))
61 type (string-to-char
62 (substring
63 signature (match-beginning 1) (match-end 1))))
64 (cond ((memq type '(?- ?1 ?>))
65 ;; routine
66 (eif-routine-to-regexp name))
67 ((= type ?=)
68 ;; attribute
69 (eif-attribute-to-regexp name))))
70 ((equal 0 (string-match eif-identifier signature))
71 ;; Assume is a class name
72 (concat eif-class-name-before (regexp-quote signature)
73 eif-class-name-after))))
74 (or regexp
75 (error "(eif-feature-signature-to-regexp): Invalid format, '%s'"
76 signature))))
77
78 (defun eif-feature-tree-command-p (class-or-signature)
79 "Display definition of CLASS-OR-SIGNATURE if a signature and return t, else return nil."
80 (if (br-in-browser) (br-to-view-window))
81 (br-feature-found-p (br-feature-file class-or-signature)
82 class-or-signature))
83
84 (defun eif-list-features (class &optional indent)
85 "Return sorted list of Eiffel feature names lexically defined in CLASS."
86 (let ((class-tag (concat "\n" class eif-type-tag-separator))
87 (features) start end)
88 (save-excursion
89 (set-buffer
90 (funcall br-find-file-noselect-function br-feature-tags-file))
91 (goto-char 1)
92 (if (not (search-forward class-tag nil t))
93 nil
94 (setq start (match-beginning 0)
95 end (if (search-forward "\^L\n" nil t)
96 (match-beginning 0)
97 (point-max)))
98 (goto-char start)
99 ;; Feature defs can occur only within a single file.
100 (while (search-forward class-tag end t)
101 (setq features (cons (br-feature-current) features)))
102 (eif-sort-features features)))))
103
104 (defun eif-get-feature-tags (feature-file feature-list)
105 "Save Eiffel feature tags defined in FEATURE-FILE to 'br-feature-tags-file'.
106 Assume FEATURE-FILE has already been read into a buffer and that
107 'br-feature-tags-init' has been called. FEATURE-LIST is the list
108 of tags to save."
109 (interactive)
110 (let ((obuf (current-buffer)))
111 (set-buffer (funcall br-find-file-noselect-function br-feature-tags-file))
112 (goto-char 1)
113 ;; Delete any prior feature tags associated with feature-file
114 (if (search-forward feature-file nil 'end)
115 (progn (forward-line -1)
116 (let ((start (point)))
117 (search-forward "\^L" nil 'end 2)
118 (backward-char 1)
119 (delete-region start (point))
120 )))
121 (if feature-list
122 (progn (insert "\^L\n" feature-file "\n")
123 (mapcar (function (lambda (tag) (insert tag "\n")))
124 feature-list)))
125 (set-buffer obuf)))
126
127 (defun eif-scan-features-in-class (class start end)
128 "Return unordered list of Eiffel feature definitions in CLASS.
129 START and END give buffer region to search."
130 (save-excursion
131 (save-restriction
132 (narrow-to-region start end)
133 (goto-char start)
134 (let ((attributes-and-routines (eif-parse-features t)))
135 (append
136 (mapcar
137 (function (lambda (routine)
138 (concat class eif-type-tag-separator routine)))
139 (cdr attributes-and-routines))
140 (mapcar
141 (function (lambda (attribute)
142 (concat class eif-type-tag-separator attribute)))
143 (car attributes-and-routines)))))))
144
145 (defun eif-sort-features (feature-list)
146 (sort feature-list 'eif-feature-lessp))
147
148 (defun eif-to-definition (&optional identifier)
149 "If point is within an Eiffel class or feature name, try to move to its definition.
150 With optional IDENTIFIER, do the same instead for it."
151 (interactive)
152 (let ((cl (or identifier (eif-find-class-name))))
153 (cond
154 ((eif-keyword-p) nil)
155 ((br-check-for-class cl))
156 ((eif-feature cl))
157 ((progn
158 (beep)
159 (message
160 "(OO-Browser): Select an Eiffel identifier to move to its definition.")
161 nil))
162 )))
163
164 ;; ************************************************************************
165 ;; Private functions
166 ;; ************************************************************************
167
168 (defun eif-export-feature-p ()
169 "Return nil unless point is within a class export clause."
170 (save-excursion
171 (let ((end (point)))
172 (beginning-of-line)
173 ;; If in a comment, return nil.
174 (if (search-forward "--" end t)
175 nil
176 (goto-char (point-min))
177 (and (re-search-forward eif-export-key-regexp end t)
178 (not (re-search-forward "^\\(inherit\\|feature\\)\\([ \t]\\|$\\)" end t)))))))
179
180 (defun eif-feature (&optional ftr)
181 "Return nil if definition is not found for optional FTR or feature declared at point."
182 (interactive)
183 (let ((class-deferred)
184 (class)
185 (deferred-p)
186 (ftr-def-class))
187 (cond ((or ftr (and (eif-export-feature-p)
188 (setq ftr (eif-to-feature-decl))))
189 (if (and (setq class-deferred (eif-get-class-name-from-source))
190 (setq class (car class-deferred)
191 deferred-p (cdr class-deferred)
192 ftr-def-class (eif-find-ancestors-feature
193 (list class) deferred-p ftr)))
194 (cond ((equal (car ftr-def-class) class) t)
195 ((equal (cdr ftr-def-class) ftr)
196 ;; Feature inherited but not renamed.
197 (message
198 "Feature '%s' of class '%s' inherited from class '%s'."
199 ftr class (car ftr-def-class)))
200 ;; Feature inherited and renamed.
201 (t (message "Feature '%s', class '%s' from feature '%s', class '%s'."
202 ftr class (cdr ftr-def-class)
203 (car ftr-def-class))
204 t))
205 (beep)
206 (message "(OO-Browser): '%s' feature not found." ftr)
207 t))
208 ((and (not ftr) (eif-feature-def-p)))
209 ;;
210 ;; Later we might add the case of a feature invocation here.
211 ;;
212 )))
213
214 (defun eif-feature-def-p ()
215 "If point is within a feature definition's name, display feature including leading comments."
216 (let ((opoint (point)))
217 (beginning-of-line)
218 (if (or (looking-at eif-routine-regexp)
219 (looking-at eif-attribute-regexp))
220 (progn (setq opoint (match-beginning eif-feature-name-grpn))
221 (eif-to-comments-begin)
222 (recenter 0)
223 (goto-char opoint)
224 t)
225 (goto-char opoint)
226 nil)))
227
228 (defun eif-feature-matches (regexp)
229 "Return an unsorted list of feature tags whose names match in part or whole to REGEXP."
230 ;; Ensure match to feature names only; also handle "^" and "$" meta-chars
231 (setq regexp
232 (concat "^\\(" eif-identifier "\\)"
233 eif-type-tag-separator
234 br-feature-type-regexp " "
235 (if (equal (substring regexp 0 1) "^")
236 (progn (setq regexp (substring regexp 1)) nil)
237 (concat "[" eif-identifier-chars "]*"))
238 (if (equal (substring regexp -1) "$")
239 (substring regexp 0 -1)
240 (concat regexp "[" eif-identifier-chars "]*"))
241 "[ \t\n\r]"))
242 (save-excursion
243 (set-buffer (funcall br-find-file-noselect-function br-feature-tags-file))
244 (goto-char 1)
245 (let ((features) start end)
246 (if (not (re-search-forward regexp nil t))
247 nil
248 (setq start (match-beginning 0)
249 end (if (search-forward "\^L\n" nil t)
250 (match-beginning 0)
251 (point-max)))
252 (goto-char start)
253 ;; Feature defs can occur only within a single file.
254 (while (re-search-forward regexp end t)
255 (backward-char) ;; Might have moved past newline.
256 (setq features (cons (br-feature-current) features))))
257 features)))
258
259 (defun eif-feature-lessp (feature1 feature2)
260 (string-lessp (eif-feature-partial-name feature1)
261 (eif-feature-partial-name feature2)))
262
263 (defun eif-feature-partial-name (signature &optional with-class for-display)
264 "Extract the feature name without its class name from feature SIGNATURE.
265 If optional WITH-CLASS is non-nil, class name and 'eif-type-tag-separator'
266 are prepended to the name returned. If optional FOR-DISPLAY is non-nil, a
267 feature type character is prepended to the name for display in a browser
268 listing."
269 (if (string-match (concat eif-type-tag-separator
270 "\\(" br-feature-type-regexp " \\)")
271 signature)
272 (let ((class (substring signature 0 (match-beginning 0)))
273 (name (substring signature (match-end 0))))
274 (cond ((and with-class for-display)
275 signature)
276 (with-class
277 (concat class eif-type-tag-separator name))
278 (for-display
279 (substring signature (match-beginning 1)))
280 (t name)))
281 signature))
282
283 (defun eif-feature-tag-class (element-tag)
284 "Extract the class name from ELEMENT-TAG."
285 (if (string-match eif-type-tag-separator element-tag)
286 (substring element-tag 0 (match-beginning 0))
287 ""))
288
289 (defun eif-find-ancestors-feature (class-list deferred-class ftr)
290 (let* ((classes class-list)
291 (cl)
292 (file)
293 (found-ftr))
294 (if (null class-list)
295 nil
296 (while (and (not found-ftr) classes)
297 (setq cl (car classes)
298 file (br-class-path cl))
299 (and file (setq found-ftr
300 (br-feature-found-p file ftr deferred-class)))
301 ;; If found-ftr is a cons cell, then only one parent class need
302 ;; be searched to look for ftr.
303 (if (consp found-ftr)
304 (setq class-list (list (car found-ftr))
305 ftr (cdr found-ftr)))
306 (setq classes (cdr classes)))
307 (cond ((consp found-ftr)
308 (eif-find-ancestors-feature class-list deferred-class ftr))
309 ((null found-ftr)
310 (eif-find-ancestors-feature
311 (apply 'append (mapcar (function
312 (lambda (cl) (br-get-parents cl)))
313 class-list))
314 deferred-class
315 ftr))
316 (t (cons cl ftr))))))
317
318 ;; Prefixed with 'eiffel' rather than 'eif' since works as a standalone
319 ;; feature in buffers whose major mode is 'eiffel-mode'. It is used by the
320 ;; browser but may also be used standalone.
321 ;;
322 (defun eiffel-find-feature (feature-name)
323 "Move point to start of feature named FEATURE-NAME in current buffer.
324 Display feature including all preceding comments at the top of the window.
325 Move point and return non-nil iff FEATURE-NAME is found."
326 (interactive "sFeature to find: ")
327 (cond ((eif-locate-feature
328 feature-name (eif-routine-to-regexp feature-name)))
329 ((eif-to-attribute feature-name)
330 (let ((opoint (point)))
331 (eif-to-comments-begin)
332 (recenter 0)
333 (goto-char opoint)
334 (back-to-indentation)
335 t))))
336
337 (defun eif-find-class-name ()
338 "Return class name that point is within, else nil."
339 (if (= (point) (point-max)) (skip-chars-backward " \t\n"))
340 (save-excursion
341 (skip-chars-forward " \t")
342 (skip-chars-backward eif-identifier-chars)
343 (skip-chars-backward " \t\n")
344 (backward-char 1)
345 (and (looking-at eif-class-name-pat)
346 (eif-set-case
347 (buffer-substring (match-beginning 2)
348 (match-end 2))))))
349
350 (defun eif-find-feature (feature-name)
351 "With point selecting a class in a listing buffer, move point to definition of FEATURE-NAME in viewer window.
352 Move point and return non-nil iff FEATURE-NAME is found."
353 (interactive "sFeature to find: ")
354 ;; If selected class is displayed, don't go to start of class
355 (if (equal (br-class-path (br-find-class-name))
356 (progn
357 (br-to-from-viewer)
358 (expand-file-name buffer-file-name)))
359 nil
360 (br-edit))
361 (if (eiffel-find-feature feature-name)
362 (progn (recenter 0)
363 t)
364 (br-to-from-viewer)
365 (and (interactive-p)
366 (progn
367 (beep)
368 (message "(OO-Browser): No '%s' feature found." feature-name)))))
369
370 (defun eif-feature-locate-p (feature-tag)
371 (let (start class)
372 (if (string-match (concat "\\`[^\]\[]+" eif-type-tag-separator)
373 feature-tag)
374 ;; First move to the proper class implementation, so that if two
375 ;; classes in the same file have the same feature signature, we still
376 ;; end up at the right one.
377 (progn
378 (setq class (substring feature-tag 0 (1- (match-end 0))))
379 (re-search-forward
380 (concat eif-class-name-before (regexp-quote class)
381 eif-class-name-after)
382 nil t)))
383 (if (not (re-search-forward
384 (eif-feature-signature-to-regexp feature-tag) nil t))
385 nil
386 (setq start (match-beginning 0))
387 (goto-char start)
388 (skip-chars-forward " \t\n")
389 (eif-to-comments-begin)
390 (recenter 0)
391 (goto-char start)
392 t)))
393
394 (defun eif-keyword-p ()
395 "Return t if point is within an Eiffel keyword, else nil."
396 (if (= (point) (point-max)) (skip-chars-backward " \t\n"))
397 (save-excursion
398 (skip-chars-forward " \t")
399 (skip-chars-backward eif-identifier-chars)
400 (and (looking-at eif-identifier)
401 (br-member-sorted-strings (buffer-substring (match-beginning 0)
402 (match-end 0))
403 eif-reserved-words))))
404
405 (defun eif-locate-feature (ftr ftr-pat)
406 (let ((opoint (point)))
407 (goto-char (point-min))
408 (if (and (re-search-forward "^feature\\([ \t]\\|$\\)" nil t)
409 (re-search-forward ftr-pat nil t))
410 (progn (goto-char (match-beginning eif-feature-name-grpn))
411 (setq opoint (point))
412 (eif-to-comments-begin)
413 (recenter 0)
414 (goto-char opoint)
415 t)
416 (goto-char opoint)
417 (and (interactive-p) (error (format "Feature '%s' not found."
418 ftr))))))
419
420 (defun eif-renamed-feature-p (ftr)
421 (goto-char (point-min))
422 (let ((rename-regexp "[ \t\n]+rename[ \t\n]")
423 (rename-match
424 (concat eif-identifier "[ \t\n]+as[ \t\n]+" ftr "[,; \t\n]"))
425 (prev-feature-nm)
426 (prev-class)
427 (parents))
428 (while (and (setq prev-feature-nm
429 (and (re-search-forward rename-regexp nil t)
430 (re-search-forward rename-match nil t)))
431 (setq prev-feature-nm
432 (buffer-substring (match-beginning 1) (match-end 1))
433 prev-class (match-beginning 0))
434 (progn (backward-char 1)
435 (eif-in-comment-p))))
436 (if prev-feature-nm
437 (progn (goto-char prev-class)
438 (setq parents (eif-get-parents-from-source buffer-file-name))
439 (if (re-search-backward (concat
440 "[^[][ \t\n]+\\("
441 (mapconcat
442 (function (lambda (cl)
443 (eif-set-case-type cl)))
444 parents
445 "\\|")
446 "\\)")
447 nil t)
448 (progn (setq prev-class (eif-set-case (buffer-substring
449 (match-beginning 1)
450 (match-end 1))))
451 (cons prev-class prev-feature-nm))
452 (beep)
453 (message
454 "(OO-Browser): Internal error - no class associated with rename clause."))))))
455
456 (defun eif-to-feature-decl ()
457 (let ((end))
458 (while (and (progn (skip-chars-backward "^, \t\n")
459 (and (not (= (preceding-char) ?,))
460 (not (looking-at "export[ \t\n]+"))))
461 (progn (skip-chars-backward " \t\n")
462 (setq end (point))
463 (beginning-of-line)
464 (if (search-forward "--" end t)
465 (progn (goto-char end)
466 (skip-chars-forward " \t\n")
467 nil)
468 (goto-char end)
469 t)))))
470 (if (looking-at "export[ \t\n]+")
471 (goto-char (match-end 0))
472 (skip-chars-forward " \t\n"))
473 (if (looking-at eif-feature-name)
474 (buffer-substring (match-beginning 0) (match-end 0))))
475
476
477 ;; ************************************************************************
478 ;; Private variables
479 ;; ************************************************************************
480
481 (defconst eif-feature-name
482 (concat
483 "\\("
484 "\\(prefix[ \t]+\"\\(not\\|\\+\\|-\\)\"\\)"
485 "\\|infix[ \t]+\"\\(div\\|mod\\|^\\|<=?\\|>=?\\|\+\\|-\\|\\*\\|/"
486 "\\|and then\\|and\\|or else\\|or\\|xor\\|implies\\)"
487 "\\|" eif-identifier "\\)")
488 "Regexp matching any Eiffel feature name.
489 Will also match class names and keywords, so tests for these should precede
490 use of this expression.")
491
492 (defconst eif-export-key-regexp
493 "\\(^[ \t]*\\|[ \t]+\\)export[ \t\n]+"
494 "Regexp matching the Eiffel export keyword in context.")
495
496 (defconst eif-class-repeat (concat "repeat[ \t]+" eif-identifier)
497 "Match to an Eiffel 'repeat <class>' phrase. Grouping 1 is class name.")
498
499 (defconst eif-exported-feature
500 (concat "\\(,\\|export[ \t\n]+\\(--.*[ \t\n]+\\)*\\)"
501 eif-feature-name "\\([ \t]*{[^}]+}\\)?"
502 "\\([ \t]*[\n,]\\|[ \t]+--\\)")
503 "Regexp to match to a feature declaration in an export clause.
504 Exclude 'repeat <class>' phrases. Feature name is grouping 3.")
505
506
507 (provide 'br-eif-ft)