4 ;; SUMMARY: OO-Browser feature browsing support.
5 ;; USAGE: GNU Emacs Lisp Library
6 ;; KEYWORDS: oop, tools
11 ;; ORIG-DATE: 20-Aug-91 at 18:16:36
12 ;; LAST-MOD: 10-May-01 at 19:08:32 by Bob Weiner
13 ;;; ************************************************************************
14 ;;; Other required Elisp libraries
15 ;;; ************************************************************************
17 (mapcar 'require '(br-c-ft hypb))
19 ;;; ************************************************************************
21 ;;; ************************************************************************
23 (defconst br-attribute-type-regexp "[=&]"
24 "Regular expression which matches the first non-whitespace character in an OO-Browser attribute listing.")
26 (defconst br-feature-type-regexp "[-+=&%>1/]"
27 "Regular expression which matches the first non-whitespace character in an OO-Browser feature listing.")
29 (defconst br-routine-type-regexp "[-+>1/]"
30 "Regular expression which matches the first non-whitespace character in an OO-Browser routine listing.")
32 ;;; ************************************************************************
34 ;;; ************************************************************************
36 (defun br-edit-feature (class feature-name &optional other-win view-only)
37 "Edit the definition of CLASS' FEATURE-NAME, optionally in some OTHER-WIN if non-nil.
38 With optional VIEW-ONLY non-nil, view the feature definition instead of editing it.
39 Return the pathname of the feature definition if found, else nil."
41 (list nil (br-feature-complete 'must-match "Edit feature definition:")
43 (let ((tag-and-file (br-feature-tag-and-file
45 ;; Assume feature-name includes prepended class in
46 ;; proper format, e.g. when called interactively.
48 (concat class "::" feature-name)))))
49 (if tag-and-file (br-edit-feature-from-tag
50 (car tag-and-file) (cdr tag-and-file) other-win view-only))))
52 (defun br-edit-feature-from-tag (feature-tag feature-path &optional other-win view-only)
53 "Edit feature for OO-Browser FEATURE-TAG of file FEATURE-PATH, optionally in OTHER-WIN if non-nil.
54 With optional VIEW-ONLY, view feature definition instead of editing it.
55 Return FEATURE-PATH if feature definition is found, else nil."
57 (cond ((and feature-path (file-readable-p feature-path))
58 (cond ((br-feature-found-p feature-path feature-tag
61 (setq buffer-read-only t)
62 ;; Handle case of already existing buffer in
65 (file-writable-p feature-path)
66 (setq buffer-read-only nil)))
68 ;; Force mode-line redisplay
69 (set-buffer-modified-p (buffer-modified-p)))
73 "(OO-Browser): No `%s' feature defined in Environment."
79 "(OO-Browser): `%s' - src file not found or not readable, %s"
80 feature-tag feature-path)
83 (t (setq feature-path nil)))
87 (defun br-find-feature (&optional feature-entry view-only other-win)
88 "Display feature definition for optional FEATURE-ENTRY in VIEW-ONLY mode if non-nil in OTHER-WIN if non-nil.
89 Return feature path if FEATURE-ENTRY is successfully displayed, nil
90 otherwise. Can also signal an error when called interactively."
92 (and (interactive-p) (setq view-only current-prefix-arg))
95 (br-feature-tag-and-file
97 (br-feature-complete 'must-match
99 "View feature definition:"
100 "Edit feature definition:"))))
101 feature-path (cdr feature-entry)
102 feature-entry (car feature-entry))
103 (br-edit-feature-from-tag feature-entry feature-path other-win view-only)))
105 (defun br-find-feature-entry ()
106 "Return feature listing entry that point is within or nil.
107 Remove any leading whitespace but leave any prefix character."
108 (if (= (point) (point-max)) (skip-chars-backward " \t\n\r"))
111 (skip-chars-forward " \t")
112 (if (or (br-at-feature-p)
113 ;; Get current feature signature, if any.
114 (br-feature-get-tag))
115 (let ((feature (br-buffer-substring
117 (progn (skip-chars-forward "^\t\n\r") (point)))))
118 (if (and (equal br-lang-prefix "objc-")
119 ;; Remove any trailing class from a category entry.
120 (string-match "([^\)]+)" feature))
121 (substring feature 0 (match-end 0))
124 (defun br-feature-entry ()
125 "Return a listing entry as displayed in the buffer (sans leading whitespace)."
128 (skip-chars-forward " \t")
130 (point) (progn (skip-chars-forward "^\t\n\r") (point)))))
132 (defun br-feature-ancestor-implementors (class-name feature-name method-flag)
133 "Display an *Implementors* buffer with ancestor implementor listings matching CLASS-NAME and FEATURE-NAME.
134 The feature is a method if METHOD-FLAG is non-nil, otherwise, it is an attribute.
135 Return the number of implementors found."
136 (br-feature-relation-implementors class-name feature-name
137 'br-feature-insert-ancestor-implementors
140 (defun br-feature-descendant-implementors (class-name feature-name method-flag)
141 "Display an *Implementors* buffer with descendant implementor listings matching CLASS-NAME and FEATURE-NAME.
142 The feature is a method if METHOD-FLAG is non-nil, otherwise, it is an attribute.
143 Return the number of implementors found."
144 (br-feature-relation-implementors class-name feature-name
145 'br-feature-insert-descendant-implementors
148 (defun br-feature-complete (&optional must-match prompt)
149 "Interactively complete feature entry if possible, and return it.
150 Optional MUST-MATCH means must match a completion table entry.
151 Optional PROMPT is the initial prompt string for the user."
153 (let ((default (br-feature-default))
154 (completion-ignore-case t)
157 ;; Prompt with possible completions of ftr-entry.
158 (setq prompt (or prompt "Feature entry:")
159 completions (br-element-completions)
163 (format "%s (default %s) " prompt default)
164 completions nil must-match)
166 (format "%s (default %s) " prompt default))))
167 (if (equal ftr-entry "") default ftr-entry)))
169 (defun br-default-class-completions ()
170 "Return completion alist of the names of all default class instances."
171 (cond ((not (and br-env-file (file-exists-p br-env-file)
172 (file-readable-p br-env-file)))
174 ((and br-default-class-tags-completions
176 (car (cdr br-default-class-tags-completions)) ;; tags last mod time
177 (apply '+ (nth 5 (file-attributes br-env-file))))
178 (equal br-env-file (car br-default-class-tags-completions)))
179 (car (cdr (cdr br-default-class-tags-completions))))
184 (delq nil (mapcar 'br-default-class-p (br-all-classes)))))
189 (br-feature-map-class-tags
190 (function (lambda (tag)
191 (br-feature-tag-name tag nil nil)))
196 (br-feature-tag-sort-list
201 (if (br-default-class-p class)
203 (br-all-classes))))))
204 elt-alist (mapcar 'list elt-list)
205 br-default-class-tags-completions
207 ;; tags last mod time
208 (apply '+ (nth 5 (file-attributes br-env-file)))
212 (defun br-element-completions ()
213 "Return completion alist of all current Environment elements."
214 (cond ((not (and br-env-file (file-exists-p br-env-file)
215 (file-readable-p br-env-file)))
217 ((and br-element-tags-completions
219 (car (cdr br-element-tags-completions)) ;; tags last mod time
220 (apply '+ (nth 5 (file-attributes br-env-file))))
221 (equal br-env-file (car br-element-tags-completions)))
222 (car (cdr (cdr br-element-tags-completions))))
224 (message "(OO-Browser): Computing element completions...")
225 (let ((elt-list (br-feature-map-all-tags
226 (function (lambda (tag)
227 (br-feature-tag-name tag t nil)))))
229 (setq elt-list (br-set-of-strings
230 (br-feature-tag-sort-list elt-list))
231 elt-alist (mapcar 'list elt-list)
232 br-element-tags-completions
234 ;; tags last mod time
235 (apply '+ (nth 5 (file-attributes br-env-file)))
237 (message "(OO-Browser): Computing element completions...Done")
240 (defun br-feature-completions ()
241 "Return completion alist of all current Environment features.
242 This excludes default class elements."
243 (cond ((not (and br-env-file (file-exists-p br-env-file)
244 (file-readable-p br-env-file)))
246 ((and br-feature-tags-completions
248 (car (cdr br-feature-tags-completions)) ;; tags last mod time
249 (apply '+ (nth 5 (file-attributes br-env-file))))
250 (equal br-env-file (car br-feature-tags-completions)))
251 (car (cdr (cdr br-feature-tags-completions))))
258 (br-feature-map-class-tags
259 (function (lambda (tag)
260 (br-feature-tag-name tag nil nil)))
262 ;; All classes except default classes.
266 (if (br-default-class-p class)
268 (br-all-classes)))))))
269 (setq ftr-list (br-set-of-strings
270 (br-feature-tag-sort-list ftr-list))
271 ftr-alist (mapcar 'list ftr-list)
272 br-feature-tags-completions
274 ;; tags last mod time
275 (apply '+ (nth 5 (file-attributes br-env-file)))
279 (defun br-feature-default ()
280 "Return a best guess default for the feature or class name at point.
281 Try to return it in class::feature format."
282 (cond ((and (br-browser-buffer-p) (br-listing-window-p))
283 (let ((ftr-tag (br-feature-get-tag)))
285 (br-feature-tag-name ftr-tag t)
286 ;; assume is a class entry
287 (br-find-class-name))))
288 ((and (equal br-lang-prefix "c++-")
289 (eq major-mode (symbol-function 'br-lang-mode)))
290 (let ((member-name) (class-name) member-elts)
291 (cond ((save-excursion (c++-feature-def-p))
293 (br-feature-signature-to-name
294 (br-buffer-substring (match-beginning 0) (match-end 0))
296 ((save-excursion (c++-skip-to-statement) (c++-feature-decl))
298 (br-feature-signature-to-name
299 (br-buffer-substring (match-beginning 0) (match-end 0))
301 (t (setq member-elts (c++-feature-at-reference-p)
302 member-name (nth 4 member-elts)
303 class-name (nth 2 member-elts))))
305 (and member-name (string-match "::" member-name)))
307 ;; We know member-name is actually a type if it contains a
308 ;; <template> expression.
309 (if (and member-name (string-match "\<" member-name))
311 (c++-normalize-template-arguments
313 (setq class-name (c++-feature-class-name))))
314 (cond ((and class-name member-name)
315 (concat class-name "::" member-name))
318 ((fboundp 'smart-c++-at-tag-p)
319 (smart-c++-at-tag-p))
320 ((fboundp 'find-tag-default)
321 (find-tag-default)))))
322 ((eq major-mode (symbol-function 'br-lang-mode))
324 (let ((lang-tag-function
325 (intern-soft (concat "smart-" br-lang-prefix "at-tag-p"))))
326 (cond ((and lang-tag-function (fboundp lang-tag-function))
327 (funcall lang-tag-function))
328 ((fboundp 'find-tag-default)
329 (find-tag-default)))))
330 (t (if (fboundp 'find-tag-default) (find-tag-default)))))
332 (defun br-feature-delete-c-comments (feature)
333 "Convert multiple whitespace characters to single spaces and remove C/C++-style comments from FEATURE (a string) and return a new result string."
334 (let* ((len (length feature))
335 (normal-feature (make-string len ?\ ))
337 (space-regexp "[ \t\n\r]+")
338 (original-syntax-table (syntax-table))
342 (set-syntax-table text-mode-syntax-table)
344 (setq chr (aref feature i))
346 ;; Convert sequences of space characters to a single space.
347 ;; GNU Emacs doesn't support optional syntax-table arg to
349 ((eq (char-syntax chr) ?\ )
350 (if (string-match space-regexp feature i)
351 (progn (setq i (match-end 0))
352 (if (not (and (> n 0)
353 (eq (aref normal-feature (1- n)) ?\ )))
359 ((and (< (setq i (1+ i)) len)
362 ((eq (aref feature i) ?/)
363 ;; Remove // style comments
365 (while (and (< i len) (not (eq (aref feature i) ?\n)))
368 ((eq (aref feature i) ?*)
369 ;; Remove C-style comments
371 (while (and (< (1+ i) len)
372 (not (and (eq (aref feature i) ?*)
373 (eq (aref feature (1+ i)) ?/)
378 (t;; Normal character
379 (aset normal-feature n chr)
380 ;; `i' was already incremented at the top of the comment removal clause.
383 (substring normal-feature 0 n)))
384 (set-syntax-table original-syntax-table))))
386 (defun br-feature-display (class-list ftr-pat &optional other-win)
387 "Display feature declaration derived from CLASS-LIST, matching FTR-PAT."
388 (let ((classes class-list)
394 (if (or (null class-list) (equal class-list '(nil)))
396 (while (and (not found-ftr) classes)
397 (setq class (car classes)
398 ftr-sig-regexp (if (equal br-lang-prefix "objc-")
400 (funcall ftr-pat class))
401 ftr-tag (br-feature-tag-signature-match
402 'identity class ftr-sig-regexp)
403 ftr-path (if ftr-tag (br-feature-tag-path ftr-tag))
404 found-ftr (if ftr-path
405 (br-edit-feature-from-tag
406 ftr-tag ftr-path other-win))
407 classes (if found-ftr nil (cdr classes))))
411 (apply 'append (mapcar (function (lambda (cl) (br-get-parents cl)))
416 (defun br-feature-display-implementors (name)
417 "Display the definition of or a list of possible implementors of element NAME.
418 Return t if one or more are found, nil otherwise."
419 (interactive "sImplementors of element named: ")
420 (let* ((implementor-tags (br-feature-implementors name))
421 (sig-count (length implementor-tags))
423 (cond ((zerop sig-count)
424 (message "(OO-Browser): No implementor matches for `%s'" name) (beep)
427 (setq tag (car implementor-tags))
428 (let ((def-file (br-feature-tag-path tag)))
430 (if (br-edit-feature-from-tag tag def-file)
432 "(OO-Browser): Found definition of `%s' in class `%s'"
434 (br-feature-tag-class tag))
437 "(OO-Browser): No implementor definitions for `%s'" name) (beep)
439 (t (br-feature-list-implementors implementor-tags name) t))))
441 (defun br-feature-found-p (buf-file feature-sig-or-tag
442 &optional deferred-class other-win regexp-flag)
443 "Search BUF-FILE for FEATURE-SIG-OR-TAG.
444 BUF-FILE may be a directory in which case the directory is simply displayed.
445 Return nil if not found, otherwise display it and return the current line number."
451 (config (current-window-configuration)))
452 (setq prev-buf (get-file-buffer buf-file))
453 (funcall br-edit-file-function buf-file other-win)
454 (if (file-directory-p buf-file)
455 (setq found-def (file-readable-p buf-file))
456 (setq prev-point (point))
458 (goto-char (point-min))
460 (cond ((or (null feature-sig-or-tag)
461 (and (br-feature-tag-p feature-sig-or-tag)
462 (null (br-feature-tag-signature feature-sig-or-tag))))
463 ;; Tag simply points to the file displayed above.
466 (br-feature-locate-p feature-sig-or-tag deferred-class))
468 (br-feature-locate-p feature-sig-or-tag regexp-flag))
469 (t (br-feature-locate-p feature-sig-or-tag)))))
471 (progn (setq found-def (br-line-number))
472 ;; Set appropriate mode for file.
474 (setq buf-file (get-file-buffer buf-file))
476 (goto-char prev-point)
478 (kill-buffer buf-file)
479 (goto-char prev-point)))
480 (br-set-window-configuration config)
484 (defun br-feature-list-attributes (class)
485 "Return sorted list of attribute tags lexically defined in CLASS."
488 (function (lambda (tag)
489 (if (string-match (concat "\\`" br-attribute-type-regexp)
490 (br-feature-tag-name tag nil t))
492 (hash-get class br-features-htable))))
494 (defun br-feature-list-routines (class)
495 "Return sorted list of routine tags lexically defined in CLASS."
498 (function (lambda (tag)
499 (if (string-match (concat "\\`" br-routine-type-regexp)
500 (br-feature-tag-name tag nil t))
502 (hash-get class br-features-htable))))
504 (defun br-feature-map-class-tags (function class)
505 "Apply FUNCTION to each feature tag from CLASS and return the non-nil results."
506 (delq nil (mapcar function (hash-get class br-features-htable))))
508 (defun br-feature-map-all-tags (function)
509 "Apply FUNCTION to all current feature tags and return a list of the non-nil results."
514 (lambda (tag-list-and-class)
515 (mapcar (function (lambda (tag)
516 (funcall function tag)))
517 (car tag-list-and-class))))
518 br-features-htable))))
520 (defun br-feature-map-tags (function regexp)
521 "Apply FUNCTION to all current feature tags whose feature name listing entries match REGEXP.
522 Return a list of the non-nil results."
527 (lambda (tag-list-and-class)
530 (if (string-match regexp (br-feature-tag-name tag nil t))
531 (funcall function tag))))
532 (car tag-list-and-class))))
533 br-features-htable))))
535 (defun br-feature-match-implementors (class feature-name)
536 "Return a list of exact matching feature tags for CLASS and FEATURE-NAME."
537 (let ((match-regexp (concat "\\`" br-feature-type-regexp " " feature-name "\\'"))
539 (if (equal br-lang-prefix "c++-")
540 ;; Eliminate friend member matches.
541 (setq match-regexp (hypb:replace-match-string "%" match-regexp "" t)))
542 (br-feature-map-class-tags
543 (function (lambda (tag)
544 (if (string-match match-regexp
545 (br-feature-tag-name tag nil t))
549 (defun br-feature-name (ftr-entry)
550 "Return name part of FTR-ENTRY."
551 (cond ((string-equal br-lang-prefix "python-")
552 (if (equal (string-match python-feature-entry-regexp ftr-entry) 0)
553 (substring ftr-entry (match-beginning 2))
555 ((equal (string-match br-feature-entry ftr-entry) 0)
556 (substring ftr-entry (match-beginning 1)))
559 (defun br-feature-set-tags-buffer ()
560 "Make the `br-feature-tags-buffer' the current buffer during the current command."
561 (if (buffer-live-p br-feature-tags-buffer)
562 (set-buffer br-feature-tags-buffer)
563 (setq br-feature-tags-buffer
564 (set-buffer (funcall br-find-file-noselect-function
565 br-feature-tags-file)))))
567 (defun br-feature-signature (&optional arg)
568 "Show the full feature signature in the viewer window.
569 With optional prefix ARG, display signatures of all features from the current
572 (let* ((buf (buffer-name))
573 (owind (selected-window))
574 (tag-list (delq nil (if arg
575 (br-feature-get-tags)
576 (list (br-feature-get-tag))))))
578 (progn (beep) (message "No elements."))
580 (switch-to-buffer (get-buffer-create (concat buf "-Elements")))
581 (setq buffer-read-only nil)
582 (buffer-disable-undo (current-buffer))
584 (mapcar (function (lambda (tag)
585 (prin1 tag (current-buffer))
586 (terpri (current-buffer))))
590 (select-window owind)
593 (defun br-feature-tag-and-file (class-and-feature-name)
594 "Return (feature-tag . feature-def-file-name) of CLASS-AND-FEATURE-NAME.
595 CLASS-AND-FEATURE-NAME should be given as class::feature-name."
596 (let ((case-fold-search)
598 ;; Find only exact matches
599 (if (string-match "::" class-and-feature-name)
600 (setq class (substring class-and-feature-name 0 (match-beginning 0))
601 name-regexp (format "\\`%s %s\\'"
602 br-feature-type-regexp
604 (substring class-and-feature-name
606 ;; Safety fallback, generally should not be used.
607 (setq name-regexp (format "\\`%s %s\\'"
608 br-feature-type-regexp
609 (regexp-quote class-and-feature-name))))
612 (function (lambda (tag)
613 (if (string-match name-regexp
614 (br-feature-tag-name tag nil t))
616 (cons tag (br-feature-tag-path tag))))))
617 (hash-get class br-features-htable)))))
619 (defun br-feature-tag-class (tag)
620 "Return from TAG the class in which a feature is defined."
623 (defun br-feature-tag-sort-list (feature-tags)
624 "Sort and return a list of FEATURE-TAGS."
625 (let ((standard-output (get-buffer-create " *Feature Tags*")))
627 (set-buffer standard-output) (setq buffer-read-only nil) (erase-buffer)
628 (mapcar (function (lambda (tag) (prin1 tag) (terpri))) feature-tags)
629 (call-process-region (point-min) (point-max) "sort" t t nil)
630 (goto-char (point-max))
632 (goto-char (point-min))
634 (goto-char (point-min))
635 (prog1 (read (current-buffer))
636 (set-buffer-modified-p nil)
637 (kill-buffer standard-output)))))
639 (defun br-feature-tag-name (tag &optional with-class for-display)
640 "Return from TAG the name of its feature.
641 The feature's class name is dropped from the name unless optional WITH-CLASS
642 is non-nil. If optional FOR-DISPLAY is non-nil, the feature's type character
643 is prepended to the name for display in a browser listing."
644 (let ((name (aref tag 1)))
645 (or for-display (setq name (substring name 2)))
647 (if (equal br-lang-prefix "objc-")
648 (setq name (concat (br-feature-tag-class tag)
649 objc-type-tag-separator name))
650 (setq name (concat (br-feature-tag-class tag) "::" name))))
653 (defun br-feature-tag-p (object)
654 "Return t if OBJECT is a feature tag, nil otherwise.
655 The predicate used is relatively loose."
656 (and (vectorp object) (= (length object) 4)))
658 (defun br-feature-tag-path (tag)
659 "Return from TAG the pathname of the file in which its feature is defined."
660 (hash-get (aref tag 3) br-feature-paths-htable))
662 (defun br-feature-tag-signature (tag)
663 "Return from TAG the source code signature of its feature."
665 (if (string-equal br-lang-prefix "python-")
666 ;; If this is a Python tag, since it contains no signature, it must be
667 ;; a module or a package tag; return nil in such a case.
669 ;; Some languages don't store signatures since the feature name is
670 ;; unique per class. In such cases, return the feature-name with its
672 (br-feature-tag-name tag nil t))))
674 (defun br-feature-tag-signature-match (function class regexp)
675 "Apply FUNCTION to the first feature tag from CLASS whose signature matches REGEXP and return the result.
676 Return nil if no matching feature tag is found."
680 (if (and tag (string-match regexp (br-feature-tag-signature tag)))
681 (throw 'found (funcall function tag)))))
682 (hash-get class br-features-htable))
685 (defun br-feature-tags-delete (class)
686 "Delete all feature tags lexically defined in CLASS."
687 (hash-delete class br-features-htable)
690 (defun br-list-features (class &optional indent)
691 "Return sorted list of feature tags lexically defined in CLASS.
692 Optional INDENT is used in C++ Environments only. INDENT > 2 indicates that
693 this is a listing of inherited features, in which case, friend features,
694 which are never inherited, are omitted from the returned list."
695 (if (or (not (equal br-lang-prefix "c++-"))
696 (null indent) (<= indent 2))
697 (hash-get class br-features-htable)
698 (let ((match-regexp (concat "\\`" br-feature-type-regexp))
700 ;; Omit C++ friend features which are not inherited since indent > 2.
701 (setq match-regexp (hypb:replace-match-string "%" match-regexp "" t))
704 (function (lambda (tag)
705 (if (string-match match-regexp
706 (br-feature-tag-name tag nil t))
708 (hash-get class br-features-htable))))))
711 ;;; OO-Browser V3 Legacy Functions Still Used
713 (defun br-feature-v3-def-file (feature-tag-regexp)
714 "Return FEATURE-DEF-FILENAME for the first OO-Browser V3 tag match of FEATURE-TAG-REGEXP, or nil.
715 Feature tags come from the file named by `br-feature-tags-file'.
717 Called exclusively by (smart-element)."
719 (br-feature-set-tags-buffer)
720 (br-feature-v3-def-file-internal feature-tag-regexp)))
722 (defun br-feature-v3-def-file-internal (feature-regexp)
723 "Return file name for the OO-Browser V3 feature matching FEATURE-REGEXP, if any.
724 Assume feature tags file is current buffer and leave point at the start of
725 matching feature tag, if any."
727 (and (re-search-forward feature-regexp nil t)
728 ;; This ensures that point is left on the same line as the feature tag
730 (goto-char (match-beginning 0))
731 (br-feature-v3-file-of-tag)))
733 (defun br-feature-v3-file-of-tag ()
734 "Return the file name for the OO-Browser V3 tag that point is within.
735 Assumes the tag table is the current buffer.
737 Called exclusively by (smart-element)."
739 (search-backward "\f" nil t)
741 (let ((start (point)))
743 (br-buffer-substring start (point)))))
745 ;;; ************************************************************************
746 ;;; Listing buffer entry tag property handling.
747 ;;; ************************************************************************
749 (if (string-lessp "19" emacs-version)
752 ;; Emacs 19 or higher buffer entry tags functions
754 (defun br-feature-add-tag (ftr-tag &optional buffer)
755 "Add FTR-TAG as a property of the existing line."
757 (br-feature-put-property (- (point) 2) (point) 'tag ftr-tag buffer))
759 (defun br-feature-clear-tags (&optional buf-nm)
760 "Erase any feature signatures saved with current buffer or optional BUF-NM."
762 (if buf-nm (set-buffer (get-buffer buf-nm)))
765 (remove-text-properties (point-min) (point-max) '(tag)))))
767 (defun br-feature-get-tag (&optional line-num-minus-one)
769 (if (numberp line-num-minus-one)
770 (goto-line (1+ line-num-minus-one)))
772 (car (cdr (memq 'tag (text-properties-at (1- (point))))))))
774 (defun br-feature-get-tags ()
776 (goto-char (point-max))
781 (setq tag (get-text-property (1- (point)) 'tag))
782 (if tag (setq tags (cons tag tags)))
783 (setq found (= (forward-line -1) 0))
787 (if (fboundp 'put-nonduplicable-text-property)
788 ;; InfoDock and XEmacs
789 (defalias 'br-feature-put-property 'put-nonduplicable-text-property)
791 (defalias 'br-feature-put-property 'put-text-property))
793 ;; Tag property is placed at end of line in case leading indent is
794 ;; removed by an OO-Browser operation. In that case, we don't want to
795 ;; lose the tag property.
796 (defun br-feature-put-tags (ftr-tags)
799 (br-feature-put-property (- (point) 2) (point) 'tag (car ftr-tags))
800 (setq ftr-tags (cdr ftr-tags))
801 (if (and ftr-tags (/= (forward-line 1) 0))
802 (error "(br-feature-put-tags): Too few lines in this buffer"))))
804 (defun br-feature-to-tag (&optional start end)
805 "Move point to the first feature tag property between optional START and END.
806 Defaults are the start and end of the buffer."
807 (goto-char (or (text-property-not-all (or start (point-min))
814 ;; Emacs 18 buffer entry tags functions
817 (defun br-feature-clear-tags (&optional buf-nm)
818 "Erase any feature signatures saved with current buffer or optional BUF-NM."
819 (put (intern (or buf-nm (buffer-name))) 'features nil))
821 (defun br-feature-get-tag (&optional line-num)
822 (or (numberp line-num)
825 (setq line-num (count-lines 1 (point)))))
826 (cdr (assq line-num (get (intern-soft (buffer-name)) 'features))))
828 (defun br-feature-get-tags ()
829 (get (intern-soft (buffer-name)) 'features))
831 (defun br-feature-put-tags (ftr-tags)
833 (let* ((line (count-lines 1 (point)))
834 (meth-alist (mapcar (function
836 (prog1 (cons line meth)
837 (setq line (1+ line)))))
839 (buf-sym (intern (buffer-name))))
840 (put buf-sym 'features
841 (nconc (get buf-sym 'features) meth-alist))))
844 ;;; ************************************************************************
845 ;;; END - Listing buffer entry tag property handling.
846 ;;; ************************************************************************
848 (defun br-feature-tags-init (env-file)
849 "Set up `br-feature-tags-file' for writing."
850 (or env-file (setq env-file br-env-file))
851 (setq br-feature-tags-completions nil
852 br-element-tags-completions nil
853 br-feature-tags-file (br-feature-tags-file-name env-file)
854 br-tags-tmp-file (concat env-file "-ETAGS"))
855 (br-feature-set-tags-buffer)
856 (buffer-disable-undo (current-buffer))
857 (make-local-variable 'make-backup-files)
858 (make-local-variable 'backup-inhibited)
859 (setq make-backup-files nil
861 buffer-read-only nil))
863 (defun br-feature-tags-file-name (env-file)
864 (concat env-file "-FTR"))
866 (defun br-feature-build-htables ()
867 "Filter out extraneous lines from feature tag entries and save `br-feature-tags-file'."
869 (br-feature-set-tags-buffer)
870 (save-buffer) ;; do a temporary save in case there is a failure below
871 (c-build-element-tags)
872 (goto-char (point-min))
873 (delete-matching-lines "^[ \t]*$")
874 (goto-char (point-min))
875 (replace-regexp "^[ \t]+\\|[ \t]+$" "")
877 (br-feature-make-htables)
878 (kill-buffer (current-buffer)))
879 ;; The feature tags files has been replaced by feature alists stored in
880 ;; main Env file, so delete it after extracting its data.
881 (if (and (file-exists-p br-feature-tags-file)
882 (file-writable-p br-feature-tags-file))
883 (delete-file br-feature-tags-file)))
885 (defun br-insert-features (feature-tag-list &optional indent)
886 "Insert feature names from FEATURE-TAG-LIST in current buffer indented INDENT columns."
887 (let ((start (point)))
889 (lambda (feature-tag)
891 (progn (if indent (indent-to indent))
892 (insert (br-feature-tag-name feature-tag nil t)
897 (br-feature-put-tags feature-tag-list))))
899 ;;; ************************************************************************
900 ;;; Private functions
901 ;;; ************************************************************************
903 (defun br-feature-current ()
904 "Extract current feature from tags file and leave point at the end of line."
906 (br-buffer-substring (point) (progn (end-of-line) (point))))
908 (defun br-feature-insert-ancestor-implementors (class-list feature-name
909 &optional depth offset count)
910 "Insert into the current buffer ancestor implementor listings matching CLASS-LIST and FEATURE-NAME.
911 Ancestor trees are inverted, i.e. parents appear below children, not above.
912 Indent each class in CLASS-LIST by optional DEPTH spaces (default is 0 in
913 order to ensure proper initialization). Offset each child level by optional
914 OFFSET spaces from its parent (which must be greater than zero, default 2).
915 COUNT is the number of implementers found. This number is returned to the
917 (or offset (setq offset 2))
918 (or depth (setq depth 0))
919 (or count (setq count 0))
920 (if (= depth 0) (setq br-tmp-class-set nil))
921 (let ((prev-expansion-str " ...")
922 parents expand-subtree tags)
926 (setq expand-subtree (br-set-cons br-tmp-class-set class)
927 parents (if expand-subtree (br-get-parents class)))
930 (and (not expand-subtree) (br-has-children-p class)
931 (insert prev-expansion-str))
933 (if (not expand-subtree) ;; repeated class
935 ;; Compute implementors list
936 (setq tags (br-feature-match-implementors class feature-name))
937 (setq count (+ count (br-feature-insert-signatures
939 ;; Indent implementors twice as much
940 ;; as class names for readability.
941 (+ depth offset offset)))))
944 (br-feature-insert-ancestor-implementors
945 parents feature-name (+ depth offset)
948 (if (= depth 0) (setq br-tmp-class-set nil))
951 (defun br-feature-insert-descendant-implementors (class-list feature-name
952 &optional depth offset count)
953 "Insert into the current buffer descendant implementor listings matching CLASS-LIST and FEATURE-NAME.
954 Indent each class in CLASS-LIST by optional DEPTH spaces (default is 0 in
955 order to ensure proper initialization). Offset each child level by optional
956 OFFSET spaces from its parent (which must be greater than zero, default 2).
957 COUNT is the number of implementers found. This number is returned to the
959 (or offset (setq offset 2))
960 (or depth (setq depth 0))
961 (or count (setq count 0))
962 (if (= depth 0) (setq br-tmp-class-set nil))
963 (let ((prev-expansion-str " ...")
964 children expand-subtree tags)
968 (setq expand-subtree (br-set-cons br-tmp-class-set class)
969 children (if expand-subtree (br-get-children class)))
972 (and (not expand-subtree) (br-has-children-p class)
973 (insert prev-expansion-str))
975 (if (not expand-subtree) ;; repeated class
977 ;; Compute implementors list
978 (setq tags (br-feature-match-implementors class feature-name))
979 (setq count (+ count (br-feature-insert-signatures
981 ;; Indent implementors twice as much
982 ;; as class names for readability.
983 (+ depth offset offset)))))
986 (br-feature-insert-descendant-implementors
987 children feature-name (+ depth offset)
990 (if (= depth 0) (setq br-tmp-class-set nil))
993 (defun br-feature-insert-signatures (tag-list indent)
994 "Insert feature signatures from feature TAG-LIST into current buffer indented INDENT columns.
995 Return the number of feature signatures inserted."
996 (let ((start (point)))
997 (mapcar (function (lambda (tag)
999 (insert (br-feature-tag-signature tag) "\n")))
1003 (br-feature-put-tags tag-list)))
1006 (defun br-feature-list-implementors (implementors name)
1007 "Display a buffer with a list of known IMPLEMENTORS of an element NAME."
1008 (interactive (list nil (read-string "List implementors of element named: ")))
1009 (let ((temp-buffer-show-function temp-buffer-show-function)
1010 (prev-class) class sig)
1012 (progn (br-to-view-window)
1013 (setq temp-buffer-show-function 'switch-to-buffer)))
1014 (with-output-to-temp-buffer "*Implementors*"
1015 ;; Next line needed because of call to `br-feature-add-tag' below.
1016 (set-buffer standard-output)
1017 (princ "Press the Action Key on any line below to display its definition:")
1019 (mapcar (function (lambda (tag)
1020 (setq class (br-feature-tag-class tag))
1021 (if (not (equal class prev-class))
1022 (progn (princ class) (terpri)
1023 (setq prev-class class)))
1024 (setq sig (br-feature-tag-signature tag))
1025 (princ " ") (princ sig)
1026 (br-feature-add-tag tag standard-output)
1028 (or implementors (br-feature-implementors name))))
1029 (select-window (or (get-buffer-window "*Implementors*") (selected-window)))
1032 (defun br-feature-make-htables ()
1033 "Convert the current buffer of OO-Browser feature tags to hash table entries."
1034 (message "Building class features index...")
1038 (goto-char (point-min))
1039 (let ((path-counter 1)
1042 (end-of-file-entries)
1043 (python (string-equal br-lang-prefix "python-"))
1044 (standard-output (get-buffer-create "*br-feature-alists*"))
1045 class entry path signature)
1047 (set-buffer standard-output) (setq buffer-read-only nil)
1051 (forward-line 1) ;; past ^L separator
1052 (setq path (read (current-buffer)))
1053 (if (not (stringp path)) (setq path (symbol-name path)))
1054 (setq paths-alist (cons (cons (int-to-string path-counter)
1058 (setq end-of-file-entries
1059 (1- (or (search-forward "\^L" nil t)
1061 (forward-line 1) ;; past pathname
1062 (while (< (point) end-of-file-entries)
1063 (if (looking-at br-tag-fields-regexp)
1065 (setq class (buffer-substring
1066 (match-beginning 1) (match-end 1))
1067 entry (buffer-substring
1068 ;; Grouping 2 match may not exist.
1069 (or (match-beginning 2)
1070 (match-beginning 3))
1073 (if (= (match-end 0) (point))
1075 (setq signature nil)
1076 (setq signature (buffer-substring
1078 (progn (end-of-line) (point))))
1080 ;; Add module name to listing entry.
1083 (substring entry 0 2)
1084 (python-module-name path) "."
1085 (substring entry 2)))))
1086 (princ (format "(%S . [%S %S %S \"%d\"])\n"
1087 class class entry signature path-counter)))
1088 (error "(OO-Browser): Invalid feature entry, `%s'"
1090 (point) (save-excursion (end-of-line) (point)))))
1092 (setq path-counter (1+ path-counter)))
1096 ;; This entry appears as the reverse of all others so that
1097 ;; we can use the literal "path-counter" as a key to look up
1098 ;; the current count.
1099 (cons (cons "path-counter" (int-to-string path-counter))
1100 (nreverse paths-alist)))
1102 (set-buffer standard-output)
1103 (if (stringp br-sort-options)
1104 ;; Sort in dictionary order using only alpha characters so that
1105 ;; feature type entry characters do not influence the ordering.
1106 (call-process-region (point-min) (point-max) "sort" t t nil "-rd")
1108 (call-process-region (point-min) (point-max) "sort" t t)
1109 (reverse-region (point-min) (point-max)))
1110 (goto-char (point-min))
1111 (princ "\(setq features-alist\n'\(\n")
1112 (goto-char (point-max))
1114 (goto-char (point-min))
1115 ;; set feature alist variables
1116 (eval (read (current-buffer)))
1117 (set-buffer-modified-p nil)
1118 (kill-buffer standard-output)
1119 (setq br-features-htable (hash-make-prepend features-alist t)
1120 br-feature-paths-htable (hash-make paths-alist t)))))
1121 (message "Building class features index...Done"))
1123 (defun br-feature-relation-implementors (class-name feature-name
1124 implementors-function method-flag)
1125 "Display an *Implementors* buffer with a subset of implementor listings related to CLASS-NAME and FEATURE-NAME, computed from IMPLEMENTORS-FUNCTION.
1126 The feature is a method if METHOD-FLAG is non-nil, otherwise, it is an attribute.
1127 Return the number of implementors found."
1128 (message "Locating definition matches for %s::%s..."
1129 class-name feature-name)
1130 (let ((temp-buffer-show-function temp-buffer-show-function)
1134 (progn (br-to-view-window)
1135 (setq temp-buffer-show-function 'switch-to-buffer)))
1136 (setq obuf (current-buffer))
1137 (set-buffer (get-buffer-create "*Implementors*"))
1138 (setq buffer-read-only nil)
1141 "Press the Action Key on any line below to display its definition:\n\n")
1142 (setq count (funcall implementors-function (list class-name) feature-name))
1143 (cond ((zerop count)
1144 ;; No implementors found
1146 (set-buffer-modified-p nil (get-buffer "*Implementors*"))
1147 (kill-buffer "*Implementors*")
1150 ;; Jump to definition and delete *Implementors* buffer.
1152 (let* ((ftr-tag (br-feature-get-tag))
1153 (ftr-class (br-feature-tag-class ftr-tag))
1154 (ftr-path (br-feature-tag-path ftr-tag)))
1155 (if (and ftr-path (br-edit-feature-from-tag ftr-tag ftr-path))
1156 (progn (message "(OO-Browser): Found the %sdefinition of %s::%s"
1157 (if (equal class-name ftr-class)
1159 class-name feature-name)
1161 (set-buffer-modified-p nil (get-buffer "*Implementors*"))
1162 (kill-buffer "*Implementors*")
1165 ;; ftr-tag not found within ftr-path; this means some
1166 ;; directory or file name within the Environment data
1167 ;; files is out of sync with the actual directory or file
1168 ;; name in use locally, e.g. when an Environment is copied
1169 ;; from one system to another and the Environment
1170 ;; directories are not updated.
1171 (with-output-to-temp-buffer "*OO-Browser Error*"
1172 (princ "The OO-Browser found an entry for `")
1173 (princ feature-name)
1174 (princ "'\nbut could not find the actual definition within\n")
1175 (princ "the source file which is supposed to define the feature.\n\n")
1176 (princ "The current OO-Browser Environment is defined by the file:\n")
1180 (princ "The Environment file mistakenly says that\n`")
1181 (princ feature-name)
1182 (princ "' is defined within the file:\n")
1186 (princ "If this source file does not exist, the cause is often\n")
1187 (princ "that an OO-Browser Environment has been copied from\n")
1188 (princ "one directory or one machine to another. In that case,\n")
1189 (princ "you should delete the Environment file and then\n")
1190 (princ "re-create it.\n"))
1192 ;; This next case should never really happen. It means the
1193 ;; ftr-tag did not match to a file name within the
1196 "(OO-Browser): No implementor definitions for `%s'" feature-name)
1199 ;; Display *Implementors* buffer for user selection.
1200 (br-pop-to-buffer "*Implementors*")
1201 (goto-char (point-min))
1203 (message "(OO-Browser): %d definitions of %s::%s found"
1204 count class-name feature-name)))
1207 ;;; ************************************************************************
1208 ;;; Private variables
1209 ;;; ************************************************************************
1211 (defconst br-feature-entry-regexp
1212 (concat br-feature-type-regexp " \\([^\t\n\r]*[^ \t\n\r]\\)")
1213 "Regexp matching a feature entry string from a browser listing buffer.")
1215 (defvar br-default-class-tags-completions nil
1216 "List of (envir-name tags-file-last-mod-time default-class-tags-completion-alist).")
1218 (defvar br-element-tags-completions nil
1219 "List of (envir-name tags-file-last-mod-time elt-tags-completion-alist).")
1221 (defvar br-feature-tags-completions nil
1222 "List of (envir-name tags-file-last-mod-time ftr-tags-completion-alist).")
1224 (defvar br-feature-tags-file nil
1225 "Pathname where object-oriented feature tags are temporarily stored during Environment builds.")
1227 (defvar br-feature-tags-buffer nil
1228 "Cached buffer attached to `br-feature-tags-file'.")
1230 (defvar br-tags-tmp-file nil
1231 "Temporary pathname used to compute non-object-oriented feature tags.")