Initial Commit
[packages] / xemacs-packages / oo-browser / br-ftr.el
1 ;;!emacs
2 ;;
3 ;; FILE:         br-ftr.el
4 ;; SUMMARY:      OO-Browser feature browsing support.
5 ;; USAGE:        GNU Emacs Lisp Library
6 ;; KEYWORDS:     oop, tools
7 ;;
8 ;; AUTHOR:       Bob Weiner
9 ;; ORG:          BeOpen.com
10 ;;
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 ;;; ************************************************************************
16
17 (mapcar 'require '(br-c-ft hypb))
18
19 ;;; ************************************************************************
20 ;;; Public variables
21 ;;; ************************************************************************
22
23 (defconst br-attribute-type-regexp "[=&]"
24   "Regular expression which matches the first non-whitespace character in an OO-Browser attribute listing.")
25
26 (defconst br-feature-type-regexp "[-+=&%>1/]"
27   "Regular expression which matches the first non-whitespace character in an OO-Browser feature listing.")
28
29 (defconst br-routine-type-regexp "[-+>1/]"
30   "Regular expression which matches the first non-whitespace character in an OO-Browser routine listing.")
31
32 ;;; ************************************************************************
33 ;;; Public functions
34 ;;; ************************************************************************
35
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."
40   (interactive
41    (list nil (br-feature-complete 'must-match "Edit feature definition:")
42          nil nil))
43   (let ((tag-and-file (br-feature-tag-and-file
44                        (if (null class)
45                            ;; Assume feature-name includes prepended class in
46                            ;; proper format, e.g. when called interactively.
47                            feature-name
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))))
51
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."
56   (let ((err))
57     (cond ((and feature-path (file-readable-p feature-path))
58            (cond ((br-feature-found-p feature-path feature-tag
59                                       nil other-win)
60                   (if view-only
61                       (setq buffer-read-only t)
62                     ;; Handle case of already existing buffer in
63                     ;; read only mode.
64                     (and buffer-read-only
65                          (file-writable-p feature-path)
66                          (setq buffer-read-only nil)))
67                   ;;
68                   ;; Force mode-line redisplay
69                   (set-buffer-modified-p (buffer-modified-p)))
70                  ((interactive-p)
71                   (setq err
72                         (format
73                          "(OO-Browser):  No `%s' feature defined in Environment."
74                          feature-tag)
75                         feature-path nil))))
76           ((interactive-p)
77            (setq err
78                  (format
79                   "(OO-Browser):  `%s' - src file not found or not readable, %s"
80                   feature-tag feature-path)
81                  feature-path nil))
82           ;; Feature not found.
83           (t (setq feature-path nil)))
84     (if err (error err))
85     feature-path))
86
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."
91   (interactive)
92   (and (interactive-p) (setq view-only current-prefix-arg))
93   (let ((feature-path))
94     (setq feature-entry
95           (br-feature-tag-and-file
96            (or feature-entry
97                (br-feature-complete 'must-match
98                                     (if view-only
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)))
104
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"))
109   (save-excursion
110     (beginning-of-line)
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
116                         (point)
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))
122             feature)))))
123
124 (defun br-feature-entry ()
125   "Return a listing entry as displayed in the buffer (sans leading whitespace)."
126   (save-excursion
127     (beginning-of-line)
128     (skip-chars-forward " \t")
129     (br-buffer-substring
130      (point) (progn (skip-chars-forward "^\t\n\r") (point)))))
131
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
138                                     method-flag))
139
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
146                                     method-flag))
147
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."
152   (interactive)
153   (let ((default (br-feature-default))
154         (completion-ignore-case t)
155         completions
156         ftr-entry)
157     ;; Prompt with possible completions of ftr-entry.
158     (setq prompt (or prompt "Feature entry:")
159           completions (br-element-completions)
160           ftr-entry
161           (if completions
162               (completing-read
163                 (format "%s (default %s) " prompt default)
164                 completions nil must-match)
165             (read-string
166               (format "%s (default %s) " prompt default))))
167     (if (equal ftr-entry "") default ftr-entry)))
168
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)))
173          nil)
174         ((and br-default-class-tags-completions
175               (eq
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))))
180         (t
181          (let ((elt-list)
182                (elt-alist)
183                (default-classes
184                  (delq nil (mapcar 'br-default-class-p (br-all-classes)))))
185            (setq elt-list
186                  (apply 'nconc
187                         (mapcar (function
188                                  (lambda (class)
189                                    (br-feature-map-class-tags
190                                     (function (lambda (tag)
191                                                 (br-feature-tag-name tag nil nil)))
192                                     class)))
193                                 default-classes)))
194            (setq elt-list
195                  (br-set-of-strings
196                   (br-feature-tag-sort-list
197                    (nconc elt-list
198                           (delq nil
199                                 (mapcar (function
200                                          (lambda (class)
201                                            (if (br-default-class-p class)
202                                                nil class)))
203                                         (br-all-classes))))))
204                  elt-alist (mapcar 'list elt-list)
205                  br-default-class-tags-completions 
206                  (list br-env-file
207                        ;; tags last mod time
208                        (apply '+ (nth 5 (file-attributes br-env-file)))
209                        elt-alist))
210            elt-alist))))
211
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)))
216          nil)
217         ((and br-element-tags-completions
218               (eq
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))))
223         (t
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)))))
228                (elt-alist))
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 
233                  (list br-env-file
234                        ;; tags last mod time
235                        (apply '+ (nth 5 (file-attributes br-env-file)))
236                        elt-alist))
237            (message "(OO-Browser):  Computing element completions...Done")
238            elt-alist))))
239
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)))
245          nil)
246         ((and br-feature-tags-completions
247               (eq
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))))
252         (t
253          (let ((ftr-alist)
254                (ftr-list
255                 (apply 'nconc
256                         (mapcar (function
257                                  (lambda (class)
258                                    (br-feature-map-class-tags
259                                     (function (lambda (tag)
260                                                 (br-feature-tag-name tag nil nil)))
261                                     class)))
262                                 ;; All classes except default classes.
263                                 (delq nil
264                                       (mapcar (function
265                                                (lambda (class)
266                                                  (if (br-default-class-p class)
267                                                      nil 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
273                  (list br-env-file
274                        ;; tags last mod time
275                        (apply '+ (nth 5 (file-attributes br-env-file)))
276                        ftr-alist))
277            ftr-alist))))
278
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)))
284            (if ftr-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))
292                   (setq member-name
293                         (br-feature-signature-to-name
294                          (br-buffer-substring (match-beginning 0) (match-end 0))
295                          t)))
296                  ((save-excursion (c++-skip-to-statement) (c++-feature-decl))
297                   (setq member-name
298                         (br-feature-signature-to-name
299                          (br-buffer-substring (match-beginning 0) (match-end 0))
300                          t)))
301                  (t (setq member-elts (c++-feature-at-reference-p)
302                           member-name (nth 4 member-elts)
303                           class-name (nth 2 member-elts))))
304            (if (or class-name
305                    (and member-name (string-match "::" member-name)))
306                nil
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))
310                  (setq member-name
311                        (c++-normalize-template-arguments
312                         member-name))
313                (setq class-name (c++-feature-class-name))))
314            (cond ((and class-name member-name)
315                   (concat class-name "::" member-name))
316                  (member-name)
317                  (class-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))
323          ;; In a code buffer
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)))))
331
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 ?\ ))
336          (n 0) (i 0)
337          (space-regexp "[ \t\n\r]+")
338          (original-syntax-table (syntax-table))
339          chr)
340     (unwind-protect
341         (progn
342           (set-syntax-table text-mode-syntax-table)
343           (while (< i len)
344             (setq chr (aref feature i)) 
345             (cond
346              ;; Convert sequences of space characters to a single space.
347              ;; GNU Emacs doesn't support optional syntax-table arg to
348              ;; `char-syntax'.
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)) ?\ )))
354                              (setq n (1+ n))))
355                 (setq i (1+ i)
356                       n (1+ n))))
357              ;;
358              ;; Remove comments
359              ((and (< (setq i (1+ i)) len)
360                    (eq chr ?/)
361                    (cond
362                     ((eq (aref feature i) ?/)
363                      ;; Remove // style comments
364                      (setq i (1+ i))
365                      (while (and (< i len) (not (eq (aref feature i) ?\n)))
366                        (setq i (1+ i)))
367                      t)
368                     ((eq (aref feature i) ?*)
369                      ;; Remove C-style comments
370                      (setq i (1+ i))
371                      (while (and (< (1+ i) len)
372                                  (not (and (eq (aref feature i) ?*)
373                                            (eq (aref feature (1+ i)) ?/)
374                                            (setq i (+ i 2)))))
375                        (setq i (1+ i)))
376                      t))))
377              ;;
378              (t;; Normal character
379               (aset normal-feature n chr)
380               ;; `i' was already incremented at the top of the comment removal clause.
381               (setq n (1+ n)))))
382           (br-delete-space
383            (substring normal-feature 0 n)))
384       (set-syntax-table original-syntax-table))))
385
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)
389          (found-ftr)
390          (ftr-sig-regexp)
391          (class)
392          (ftr-tag)
393          (ftr-path))
394     (if (or (null class-list) (equal class-list '(nil)))
395         nil
396       (while (and (not found-ftr) classes)
397         (setq class (car classes)
398               ftr-sig-regexp (if (equal br-lang-prefix "objc-")
399                                  ftr-pat
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))))
408       (if found-ftr
409           (or class t)
410         (br-feature-display
411          (apply 'append (mapcar (function (lambda (cl) (br-get-parents cl)))
412                                 class-list))
413          ftr-pat
414          other-win)))))
415
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))
422          tag)
423     (cond ((zerop sig-count)
424            (message "(OO-Browser):  No implementor matches for `%s'" name) (beep)
425            nil)
426           ((= sig-count 1)
427            (setq tag (car implementor-tags))
428            (let ((def-file (br-feature-tag-path tag)))
429              (if def-file
430                  (if (br-edit-feature-from-tag tag def-file)
431                      (progn (message
432                              "(OO-Browser):  Found definition of `%s' in class `%s'"
433                              name
434                              (br-feature-tag-class tag))
435                             t))
436                (message
437                 "(OO-Browser):  No implementor definitions for `%s'" name) (beep)
438                nil)))
439           (t (br-feature-list-implementors implementor-tags name) t))))
440
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."
446   (if buf-file
447       (let ((found-def)
448             (opoint (point))
449             (prev-buf)
450             (prev-point)
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))
457           (widen)
458           (goto-char (point-min))
459           (setq found-def 
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.
464                        t)
465                       (deferred-class
466                         (br-feature-locate-p feature-sig-or-tag deferred-class))
467                       (regexp-flag
468                        (br-feature-locate-p feature-sig-or-tag regexp-flag))
469                       (t (br-feature-locate-p feature-sig-or-tag)))))
470         (if found-def
471             (progn (setq found-def (br-line-number))
472                    ;; Set appropriate mode for file.
473                    (br-major-mode))
474           (setq buf-file (get-file-buffer buf-file))
475           (if prev-buf
476               (goto-char prev-point)
477             (if buf-file
478                 (kill-buffer buf-file)
479               (goto-char prev-point)))
480           (br-set-window-configuration config)
481           (goto-char opoint))
482         found-def)))
483
484 (defun br-feature-list-attributes (class)
485   "Return sorted list of attribute tags lexically defined in CLASS."
486   (delq nil 
487         (mapcar
488          (function (lambda (tag)
489                      (if (string-match (concat "\\`" br-attribute-type-regexp)
490                                        (br-feature-tag-name tag nil t))
491                          tag)))
492          (hash-get class br-features-htable))))
493
494 (defun br-feature-list-routines (class)
495   "Return sorted list of routine tags lexically defined in CLASS."
496   (delq nil
497         (mapcar
498          (function (lambda (tag)
499                      (if (string-match (concat "\\`" br-routine-type-regexp)
500                                        (br-feature-tag-name tag nil t))
501                          tag)))
502          (hash-get class br-features-htable))))
503
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))))
507
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."
510   (delq nil
511         (apply 'nconc
512                (hash-map
513                 (function
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))))
519
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."
523   (delq nil
524         (apply 'nconc
525                (hash-map
526                 (function
527                  (lambda (tag-list-and-class)
528                    (mapcar (function
529                             (lambda (tag)
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))))
534
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 "\\'"))
538         (case-fold-search))
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))
546                      tag)))
547      class)))
548
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))
554            ""))
555         ((equal (string-match br-feature-entry ftr-entry) 0)
556          (substring ftr-entry (match-beginning 1)))
557         (t "")))
558
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)))))
566
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
570 listing buffer."
571   (interactive "P")
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))))))
577     (if (null tag-list)
578         (progn (beep) (message "No elements."))
579       (br-to-view-window)
580       (switch-to-buffer (get-buffer-create (concat buf "-Elements")))
581       (setq buffer-read-only nil)
582       (buffer-disable-undo (current-buffer))
583       (erase-buffer)
584       (mapcar (function (lambda (tag)
585                           (prin1 tag (current-buffer))
586                           (terpri (current-buffer))))
587               tag-list)
588       (br-major-mode)
589       (goto-char 1)
590       (select-window owind)
591       (message ""))))
592
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)
597         class name-regexp)
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
603                                   (regexp-quote
604                                    (substring class-and-feature-name
605                                               (match-end 0)))))
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))))
610     (catch 'found
611       (mapcar
612        (function (lambda (tag)
613                    (if (string-match name-regexp
614                                      (br-feature-tag-name tag nil t))
615                        (throw 'found
616                               (cons tag (br-feature-tag-path tag))))))
617        (hash-get class br-features-htable)))))
618
619 (defun br-feature-tag-class (tag)
620   "Return from TAG the class in which a feature is defined."
621   (aref tag 0))
622
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*")))
626     (save-excursion
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))
631       (princ "\n\)\n")
632       (goto-char (point-min))
633       (princ "\(\n")
634       (goto-char (point-min))
635       (prog1 (read (current-buffer))
636         (set-buffer-modified-p nil)
637         (kill-buffer standard-output)))))
638
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)))
646     (if with-class
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))))
651     name))
652
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)))
657
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))
661
662 (defun br-feature-tag-signature (tag)
663   "Return from TAG the source code signature of its feature."
664   (or (aref tag 2)
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.
668           nil
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
671         ;; category prefix.
672       (br-feature-tag-name tag nil t))))
673
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."
677   (catch 'found
678     (mapcar (function
679              (lambda (tag)
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))
683     nil))
684
685 (defun br-feature-tags-delete (class)
686   "Delete all feature tags lexically defined in CLASS."
687   (hash-delete class br-features-htable)
688   nil)
689
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))
699           (case-fold-search))
700       ;; Omit C++ friend features which are not inherited since indent > 2.
701       (setq match-regexp (hypb:replace-match-string "%" match-regexp "" t))
702       (delq nil
703             (mapcar
704              (function (lambda (tag)
705                          (if (string-match match-regexp
706                                            (br-feature-tag-name tag nil t))
707                              tag)))
708              (hash-get class br-features-htable))))))
709
710 ;;;
711 ;;; OO-Browser V3 Legacy Functions Still Used
712 ;;;
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'.
716
717 Called exclusively by (smart-element)."
718   (save-excursion
719     (br-feature-set-tags-buffer)
720     (br-feature-v3-def-file-internal feature-tag-regexp)))
721
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."
726   (goto-char 1)
727   (and (re-search-forward feature-regexp nil t)
728        ;; This ensures that point is left on the same line as the feature tag
729        ;; which is found.
730        (goto-char (match-beginning 0))
731        (br-feature-v3-file-of-tag)))
732
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.
736
737 Called exclusively by (smart-element)."
738   (save-excursion
739     (search-backward "\f" nil t)
740     (forward-line 1)
741     (let ((start (point)))
742       (end-of-line)
743       (br-buffer-substring start (point)))))
744
745 ;;; ************************************************************************
746 ;;; Listing buffer entry tag property handling.
747 ;;; ************************************************************************
748
749 (if (string-lessp "19" emacs-version)
750     (progn
751       ;;
752       ;; Emacs 19 or higher buffer entry tags functions
753       ;;
754 (defun br-feature-add-tag (ftr-tag &optional buffer)
755   "Add FTR-TAG as a property of the existing line."
756   (end-of-line)
757   (br-feature-put-property (- (point) 2) (point) 'tag ftr-tag buffer))
758
759 (defun br-feature-clear-tags (&optional buf-nm)
760   "Erase any feature signatures saved with current buffer or optional BUF-NM."
761   (save-excursion
762     (if buf-nm (set-buffer (get-buffer buf-nm)))
763     (save-restriction
764       (widen)
765       (remove-text-properties (point-min) (point-max) '(tag)))))
766
767 (defun br-feature-get-tag (&optional line-num-minus-one)
768   (save-excursion
769     (if (numberp line-num-minus-one)
770         (goto-line (1+ line-num-minus-one)))
771     (end-of-line)
772     (car (cdr (memq 'tag (text-properties-at (1- (point))))))))
773
774 (defun br-feature-get-tags ()
775   (save-excursion
776     (goto-char (point-max))
777     (let ((found t)
778           (tags)
779           tag)
780       (while found
781         (setq tag (get-text-property (1- (point)) 'tag))
782         (if tag (setq tags (cons tag tags)))
783         (setq found (= (forward-line -1) 0))
784         (end-of-line))
785       tags)))
786
787 (if (fboundp 'put-nonduplicable-text-property)
788     ;; InfoDock and XEmacs
789 (defalias 'br-feature-put-property 'put-nonduplicable-text-property)
790   ;; GNU Emacs
791   (defalias 'br-feature-put-property 'put-text-property))
792
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)
797   (while ftr-tags
798     (end-of-line)
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"))))
803
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))
808                                         (or end (point-max))
809                                         'tag nil)
810                  (point))))
811       )
812
813   ;;
814   ;; Emacs 18 buffer entry tags functions
815   ;;
816
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))
820
821   (defun br-feature-get-tag (&optional line-num)
822     (or (numberp line-num)
823         (save-excursion
824           (beginning-of-line)
825           (setq line-num (count-lines 1 (point)))))
826     (cdr (assq line-num (get (intern-soft (buffer-name)) 'features))))
827
828   (defun br-feature-get-tags ()
829     (get (intern-soft (buffer-name)) 'features))
830
831   (defun br-feature-put-tags (ftr-tags)
832     (beginning-of-line)
833     (let* ((line (count-lines 1 (point)))
834            (meth-alist (mapcar (function
835                                 (lambda (meth)
836                                   (prog1 (cons line meth)
837                                     (setq line (1+ line)))))
838                                ftr-tags))
839            (buf-sym (intern (buffer-name))))
840       (put buf-sym 'features
841            (nconc (get buf-sym 'features) meth-alist))))
842 )
843
844 ;;; ************************************************************************
845 ;;; END - Listing buffer entry tag property handling.
846 ;;; ************************************************************************
847
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
860         backup-inhibited t
861         buffer-read-only nil))
862
863 (defun br-feature-tags-file-name (env-file)
864   (concat env-file "-FTR"))
865
866 (defun br-feature-build-htables ()
867   "Filter out extraneous lines from feature tag entries and save `br-feature-tags-file'."
868   (save-excursion
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]+$" "")
876     (save-buffer)
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)))
884
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)))
888     (mapcar (function
889              (lambda (feature-tag)
890                (if feature-tag
891                    (progn (if indent (indent-to indent))
892                           (insert (br-feature-tag-name feature-tag nil t)
893                                   "\n")))))
894             feature-tag-list)
895     (save-excursion
896       (goto-char start)
897       (br-feature-put-tags feature-tag-list))))
898
899 ;;; ************************************************************************
900 ;;; Private functions
901 ;;; ************************************************************************
902
903 (defun br-feature-current ()
904   "Extract current feature from tags file and leave point at the end of line."
905   (beginning-of-line)
906   (br-buffer-substring (point) (progn (end-of-line) (point))))
907
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
916 caller."
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)
923     (mapcar
924       (function
925         (lambda (class)
926           (setq expand-subtree (br-set-cons br-tmp-class-set class)
927                 parents (if expand-subtree (br-get-parents class)))
928           (indent-to depth)
929           (insert class)
930           (and (not expand-subtree) (br-has-children-p class)
931                (insert prev-expansion-str))
932           (insert "\n")
933           (if (not expand-subtree) ;; repeated class
934               nil
935             ;; Compute implementors list
936             (setq tags (br-feature-match-implementors class feature-name))
937             (setq count (+ count (br-feature-insert-signatures
938                                   tags
939                                   ;; Indent implementors twice as much
940                                   ;; as class names for readability.
941                                   (+ depth offset offset)))))
942           (if parents
943               (setq count (+ count
944                              (br-feature-insert-ancestor-implementors
945                               parents feature-name (+ depth offset)
946                               offset 0))))))
947       class-list))
948   (if (= depth 0) (setq br-tmp-class-set nil))
949   count)
950
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
958 caller."
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)
965     (mapcar
966       (function
967         (lambda (class)
968           (setq expand-subtree (br-set-cons br-tmp-class-set class)
969                 children (if expand-subtree (br-get-children class)))
970           (indent-to depth)
971           (insert class)
972           (and (not expand-subtree) (br-has-children-p class)
973                (insert prev-expansion-str))
974           (insert "\n")
975           (if (not expand-subtree) ;; repeated class
976               nil
977             ;; Compute implementors list
978             (setq tags (br-feature-match-implementors class feature-name))
979             (setq count (+ count (br-feature-insert-signatures
980                                   tags
981                                   ;; Indent implementors twice as much
982                                   ;; as class names for readability.
983                                   (+ depth offset offset)))))
984           (if children
985               (setq count (+ count
986                              (br-feature-insert-descendant-implementors
987                               children feature-name (+ depth offset)
988                               offset 0))))))
989       class-list))
990   (if (= depth 0) (setq br-tmp-class-set nil))
991   count)
992
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)
998                         (indent-to indent)
999                         (insert (br-feature-tag-signature tag) "\n")))
1000             tag-list)
1001     (save-excursion
1002       (goto-char start)
1003       (br-feature-put-tags tag-list)))
1004   (length tag-list))
1005
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)
1011     (if (br-in-browser)
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:")
1018       (terpri) (terpri)
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)
1027                           (terpri)))
1028               (or implementors (br-feature-implementors name))))
1029     (select-window (or (get-buffer-window "*Implementors*") (selected-window)))
1030     (forward-line 2)))
1031
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...")
1035   (save-excursion
1036     (save-restriction
1037       (widen)
1038       (goto-char (point-min))
1039       (let ((path-counter 1)
1040             (paths-alist)
1041             (features-alist)
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)
1046         (save-excursion
1047           (set-buffer standard-output) (setq buffer-read-only nil)
1048           (erase-buffer))
1049         (condition-case ()
1050             (while t
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)
1055                                             path)
1056                                       paths-alist))
1057               (save-excursion
1058                 (setq end-of-file-entries
1059                       (1- (or (search-forward "\^L" nil t)
1060                               (point-max)))))
1061               (forward-line 1) ;; past pathname
1062               (while (< (point) end-of-file-entries)
1063                 (if (looking-at br-tag-fields-regexp)
1064                     (progn
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))
1071                                    (match-end 3)))
1072                       (end-of-line)
1073                       (if (= (match-end 0) (point))
1074                           ;; No signature
1075                           (setq signature nil)
1076                         (setq signature (buffer-substring
1077                                          (match-end 0)
1078                                          (progn (end-of-line) (point))))
1079                         (if python
1080                             ;; Add module name to listing entry.
1081                             (setq entry
1082                                   (concat
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'"
1089                          (buffer-substring
1090                           (point) (save-excursion (end-of-line) (point)))))
1091                 (forward-line 1))
1092               (setq path-counter (1+ path-counter)))
1093           (end-of-file nil))
1094
1095         (setq paths-alist
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)))
1101
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")
1107           ;; MS OSes
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))
1113         (princ "\n\)\)\n")
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"))
1122
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)
1131         count
1132         obuf)
1133     (if (br-in-browser)
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)
1139     (erase-buffer)
1140     (insert
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
1145            (message "")
1146            (set-buffer-modified-p nil (get-buffer "*Implementors*"))
1147            (kill-buffer "*Implementors*")
1148            (set-buffer obuf))
1149           ((= count 1)
1150            ;; Jump to definition and delete *Implementors* buffer.
1151            (br-feature-to-tag)
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)
1158                                      "" "inherited ")
1159                                  class-name feature-name)
1160                         nil)
1161                (set-buffer-modified-p nil (get-buffer "*Implementors*"))
1162                (kill-buffer "*Implementors*")
1163                (set-buffer obuf)
1164                (if ftr-path
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")
1177                      (princ "  ")
1178                      (prin1 br-env-file)
1179                      (terpri) (terpri)
1180                      (princ "The Environment file mistakenly says that\n`")
1181                      (princ feature-name)
1182                      (princ "' is defined within the file:\n")
1183                      (princ "  ")
1184                      (prin1 ftr-path)
1185                      (terpri) (terpri)
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"))
1191
1192                  ;; This next case should never really happen.  It means the
1193                  ;; ftr-tag did not match to a file name within the
1194                  ;; OOBR-FTR file.
1195                  (message
1196                   "(OO-Browser):  No implementor definitions for `%s'" feature-name)
1197                  (beep)))))
1198           (t
1199            ;; Display *Implementors* buffer for user selection.
1200            (br-pop-to-buffer "*Implementors*")
1201            (goto-char (point-min))
1202            (forward-line 2)
1203            (message "(OO-Browser):  %d definitions of %s::%s found"
1204                     count class-name feature-name)))
1205     count))
1206
1207 ;;; ************************************************************************
1208 ;;; Private variables
1209 ;;; ************************************************************************
1210
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.")
1214
1215 (defvar br-default-class-tags-completions nil
1216   "List of (envir-name tags-file-last-mod-time default-class-tags-completion-alist).")
1217
1218 (defvar br-element-tags-completions nil
1219   "List of (envir-name tags-file-last-mod-time elt-tags-completion-alist).")
1220
1221 (defvar br-feature-tags-completions nil
1222   "List of (envir-name tags-file-last-mod-time ftr-tags-completion-alist).")
1223
1224 (defvar br-feature-tags-file nil
1225   "Pathname where object-oriented feature tags are temporarily stored during Environment builds.")
1226
1227 (defvar br-feature-tags-buffer nil
1228   "Cached buffer attached to `br-feature-tags-file'.")
1229
1230 (defvar br-tags-tmp-file nil
1231   "Temporary pathname used to compute non-object-oriented feature tags.")
1232
1233 (provide 'br-ftr)