home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / browser2.zip / br-tags.el < prev    next >
Lisp/Scheme  |  1995-02-17  |  13KB  |  367 lines

  1. ;;; CLASS BROWSER FOR C++
  2. ;;; $Id: br-tags.el,v 3.1 1995/02/17 18:19:36 mmann Exp $
  3. ;;;
  4. ;;; **********************************************************************
  5. ;;; Copyright (C) 1993, 1994 Gerd Moellmann. All rights reserved.
  6. ;;; Altenbergstr. 6, D-40235 Duesseldorf, Germany
  7. ;;; 100025.3303@COMPUSERVE.COM
  8. ;;; Suggestions, comments and requests for improvements are welcome.
  9. ;;; **********************************************************************
  10. ;;;
  11. ;;; This version works with both Emacs version 18 and 19, and I want
  12. ;;; to keep it that way. It requires the CL-19 Common Lisp compatibility
  13. ;;; package for Emacs 18 and 19.
  14. ;;;
  15. ;;; This file contains the code for tags like functions.
  16. ;;; 
  17.  
  18. ;; This file may be made part of the Emacs distribution at the option
  19. ;; of the FSF.
  20.  
  21. ;; This code is distributed in the hope that it will be useful,
  22. ;; but WITHOUT ANY WARRANTY.  No author or distributor
  23. ;; accepts responsibility to anyone for the consequences of using it
  24. ;; or for whether it serves any particular purpose or works at all,
  25. ;; unless he says so in writing.  Refer to the GNU Emacs General Public
  26. ;; License for full details.
  27.  
  28. ;; Everyone is granted permission to copy, modify and redistribute
  29. ;; this code, but only under the conditions described in the
  30. ;; GNU Emacs General Public License.   A copy of this license is
  31. ;; supposed to have been given to you along with GNU Emacs so you
  32. ;; can know your rights and responsibilities.  It should be in a
  33. ;; file named COPYING.  Among other things, the copyright notice
  34. ;; and this notice must be preserved on all copies.
  35.  
  36. (require 'cl-19 "cl")
  37. (require 'backquote)
  38. (require 'br-macro)
  39. (require 'br-struc)
  40.  
  41.  
  42. ;;;
  43. ;;; Return a list (CLASS INDEX MEMBER) if CLASS contains a member
  44. ;;; named NAME.     INDEX is the index of the member list in which
  45. ;;; the member is found.
  46. ;;; ###FIXME
  47. ;;; This can now be done much more efficient.
  48. ;;; 
  49.  
  50. (defun browse-contains-member (class name)
  51.   (do ((list member-lists (cdr list))
  52.        (found))
  53.       ((or (null list) found) found)
  54.     (do ((mlist (funcall (car list) class) (cdr mlist)))
  55.     ((null mlist))
  56.       (and (string= name (member-name (car mlist)))
  57.        (setq found (list class (car list) (car mlist)))))))
  58.  
  59. ;;;
  60. ;;; Return an ALIST with elements (CLASS-NAME . (CLASS LIST NAME))
  61. ;;; where each element describes on occurrence of member NAME in
  62. ;;; tree TREE.    CLASS-NAME is the name of the class in which
  63. ;;; the member was found.  The CDR of the ACONS is described
  64. ;;; above in function BROWSE-CONTAINS-MEMBER.
  65. ;;; 
  66.  
  67. (defun browse-member-alist (tree tree-header name)
  68.   (cond (browse-fast-member-lookup
  69.      (browse-fast-member-alist tree-header name))
  70.     (t
  71.      (browse-slow-member-alist tree name))))
  72.  
  73. (defun browse-fast-member-alist (tree-header name)
  74.   (let* ((sym (intern-soft name (browse-member-obarray tree-header)))
  75.      class-list
  76.      alist)
  77.     (when sym
  78.       (dolist (info (get sym 'info) alist)
  79.     (unless (memq (first info) class-list)
  80.       (setf alist (acons (class-name (tree-class (first info)))
  81.                  info
  82.                  alist)
  83.         class-list (cons (first info) class-list)))))))
  84.  
  85. (defun browse-slow-member-alist (tree name &optional alist)
  86.   (do* ((list tree (cdr list))
  87.     (root (car list) (car list)))
  88.       ((null list) alist)
  89.     (let ((found (browse-contains-member root name)))
  90.       (and found
  91.        (not (assoc (class-name (tree-class root)) alist))
  92.        (setq alist (acons (class-name (tree-class root)) found alist)))
  93.       (setq alist
  94.         (browse-slow-member-alist (tree-subclasses root) name alist)))))
  95.  
  96. ;;;
  97. ;;; Find a buffer containing a class tree.  First, the tree
  98. ;;; buffer is searched.     If found, the tree stored there is returned.
  99. ;;; Second, the default member buffer is searched and its tree
  100. ;;; returned.
  101. ;;; 
  102.  
  103. (defun browse-find-tree ()
  104.   "Find a buffer containing of class tree and return it."
  105.   (let* ((buffer (tree-choose-buffer)))
  106.     (if buffer (values (browse-@value '@tree buffer)
  107.                (browse-@value '@header buffer)
  108.                buffer))))
  109.       
  110. ;;;
  111. ;;; Read a C identifier from the current buffer like the
  112. ;;; tags facility does. Return the string read.  This functions relies
  113. ;;; on dynamic scoping (COMPLETION-IGNORE-CASE).
  114. ;;; 
  115.  
  116. (defun* browse-tags-read-name (header prompt)
  117.   "Read a C++ identifier from point and return it as string."
  118.   (save-excursion
  119.     (let* (start
  120.        (members (browse-member-obarray header)))
  121.       (multiple-value-bind (class-name member-name)
  122.       (browse-tags-read-member-and-class-name)
  123.     (unless member-name (error "No member name at point."))
  124.     (if members
  125.         (let* ((name (browse-completion-ignoring-case
  126.               (completing-read prompt members nil
  127.                        nil member-name))))
  128.           (unless (eq t (try-completion name members))
  129.         (if (y-or-n-p (concat "No exact match found. Try substrings? "))
  130.             (progn
  131.               (setq name 
  132.                 (symbol-name 
  133.                  (or (first (browse-tags-match-list 
  134.                      members (regexp-quote name) name))
  135.                  (error "Sorry, nothing found.")))))
  136.           (error "Canceled.")))
  137.           (values class-name name))
  138.       (values class-name (read-from-minibuffer prompt member-name)))))))
  139.  
  140. ;;;
  141. ;;; Return (CLASS-NAME MEMBER-NAME) from point. If there is no
  142. ;;; CLASS-NAME, return NIL for this.
  143. ;;; 
  144.  
  145. (defun browse-tags-read-member-and-class-name ()
  146.   (save-excursion
  147.     (skip-chars-backward "a-zA-Z0-9_: \t")
  148.     (skip-chars-forward " \t")
  149.  
  150.     (let* ((start (point))
  151.        (name (progn (skip-chars-forward "a-zA-Z0-9_")
  152.             (buffer-substring start (point))))
  153.        class)
  154.  
  155.       (while (looking-at "[ \t]*::")
  156.     (skip-chars-forward " \t:")
  157.     (setq start (point))
  158.     (skip-chars-forward "a-zA-Z0-9_")
  159.     (shiftf class name (buffer-substring start (point))))
  160.  
  161.       (values class name))))
  162.  
  163. ;;;
  164. ;;; Choose a class from a list of classes. Return an INFO list
  165. ;;; (TREE MEMBER-LIST MEMBER).
  166. ;;; 
  167.  
  168. (defun browse-tags-choose-class (tree header name initial-class-name)
  169.   (let ((alist (or (browse-member-alist tree header name)
  170.            (error "No classes with member `%s' found!" name))))
  171.     (browse-completion-ignoring-case
  172.       (if (null (second alist))
  173.       (cdr (first alist))
  174.     (if (browse-emacs-19-p) (push ?\? unread-command-events)
  175.       (setq unread-command-char ?\?))
  176.     (cdr (assoc (completing-read "In class: "
  177.                      alist nil t initial-class-name)
  178.             alist))))))
  179.  
  180. ;;;
  181. ;;; If VIEW is T, view, else find the an occurrence of a member.
  182. ;;; If DEFINITION is T find or view the member definition else
  183. ;;; its declaration.  This function reads the member's name from
  184. ;;; the current buffer like FIND-TAG.  It then prepares a
  185. ;;; completion list of all classes containing a member with the
  186. ;;; given name and lets the user choose the class to use.  As a
  187. ;;; last step, a tags search is performed that positions point
  188. ;;; on the member declaration or definition.
  189. ;;; 
  190.  
  191. (defun browse-tags-member-search (view definition &optional fix-name)
  192.   (multiple-value-bind
  193.       (tree header tree-buffer) (browse-find-tree)
  194.     (unless tree (error "No class tree."))
  195.  
  196.     (let* ((marker (point-marker)) class-name (name fix-name) info)
  197.       (unless name
  198.     (multiple-value-setq (class-name name)
  199.       (browse-tags-read-name
  200.        header
  201.        (concat (if view "View" "Find") " member "
  202.            (if definition "definition" "declaration") ": "))))
  203.  
  204.       (setq info (browse-tags-choose-class tree header name class-name))
  205.       (browse-tags-push-position marker info)
  206.       
  207.       ;; Goto the occurrence of the member
  208.       (member-goto view definition info
  209.            header (browse-@value '@tags-filename tree-buffer))
  210.  
  211.       ;; Record position jumped to
  212.       (browse-tags-push-position (point-marker) info t))))
  213.  
  214. ;;;
  215. ;;; View definition or declaration of member at point.
  216. ;;; 
  217.  
  218. ;;###autoload
  219. (defun browse-tags-view (arg)
  220.   "View definition for member at point (declaration with prefix)."
  221.   (interactive "P")
  222.   (browse-tags-member-search t (not arg)))
  223.  
  224. ;;;
  225. ;;; Find definition or declaration for member at point.
  226. ;;; 
  227.  
  228. ;;###autoload
  229. (defun browse-tags-find (arg)
  230.   "Find definition for member at point (declaration with prefix)."
  231.   (interactive "P")
  232.   (browse-tags-member-search nil (not arg)))
  233.  
  234. ;;;
  235. ;;; Select or create member buffer described by INFO. 
  236. ;;; 
  237.  
  238. (defun browse-tags-display-member-buffer (tree-buffer info)
  239.   (let ((buffer (get-buffer member-buffer-name)))
  240.     (cond ((null buffer)
  241.        (set-buffer tree-buffer)
  242.        (switch-to-buffer (member-display (second info) nil (first info))))
  243.       (t
  244.        (switch-to-buffer buffer)
  245.        (setq @displayed-class (first info)
  246.          @accessor (second info)
  247.          @member-list (funcall @accessor @displayed-class))
  248.        (member-redisplay)))
  249.     (member-set-point-to-member (member-name (third info)))))
  250.  
  251. ;;;
  252. ;;; Display a member buffer for the identifier under the cursor.
  253. ;;; 
  254.  
  255. (defun browse-tags-find-member-buffer (&optional fix-name)
  256.   (interactive)
  257.   (multiple-value-bind
  258.       (tree header tree-buffer) (browse-find-tree)
  259.     (unless tree (error "No class tree."))
  260.     (let* ((marker (point-marker)) class-name (name fix-name) info)
  261.       (unless name
  262.     (multiple-value-setq (class-name name)
  263.       (browse-tags-read-name header (concat "Find member list of: "))))
  264.       (setq info (browse-tags-choose-class tree header name class-name))
  265.       (browse-tags-push-position marker info)
  266.       (browse-tags-display-member-buffer tree-buffer info))))
  267.  
  268. ;;;
  269. ;;; Build a list of all member symbols matching NAME or REGEXP in the
  270. ;;; obarray MEMBERS.  Both NAME and REGEXP may be NIL in which case
  271. ;;; exact or regexp matches are not performed.
  272. ;;; 
  273.  
  274. (defun browse-tags-match-list (members regexp &optional name)
  275.   (nconc (when name
  276.        (loop for s being the symbols of members
  277.          when (string= name s) collect s))
  278.      (when regexp
  279.        (loop for s being the symbols of members
  280.          when (string-match regexp (symbol-name s))
  281.          collect s))))
  282.  
  283. ;;;
  284. ;;; Display a list of all members matched by a regular expression.
  285. ;;;
  286.  
  287. (defun browse-tags-apropos ()
  288.   "Display a list of all members matched by REGEXP."
  289.   (interactive)
  290.   (let* ((buffer (or (tree-choose-buffer) (error "No tree buffer.")))
  291.      (header (browse-@value '@header buffer))
  292.      (members (browse-member-obarray header))
  293.      (regexp (read-from-minibuffer "List members matching regexp: ")))
  294.     (with-output-to-temp-buffer (concat "*Apropos Members*")
  295.       (set-buffer standard-output)
  296.       (erase-buffer)
  297.       (insert "Members matching \"" regexp "\":\n")
  298.       (loop for s in (browse-tags-match-list members regexp) do
  299.         (loop for info in (get s 'info) do
  300.           (browse-tags-insert-member-info info))))))
  301.  
  302. ;;;
  303. ;;; Display a lists of members in FILENAME. Prepare a file list,
  304. ;;; let user select a file, get member list and check member declaration
  305. ;;; and definition file against file.
  306. ;;;
  307.  
  308. (defun browse-tags-list ()
  309.   (interactive)
  310.   (let* ((buffer (or (tree-choose-buffer)
  311.              (error "No tree buffer.")))
  312.      (files (save-excursion (set-buffer buffer)
  313.                 (tree-files-obarray)))
  314.      (file (completing-read "List members in file: " files nil t))
  315.      (header (browse-@value '@header buffer))
  316.      (members (browse-member-obarray header)))
  317.  
  318.     (with-output-to-temp-buffer (concat "*Members in file " file "*")
  319.       (set-buffer standard-output)
  320.       (loop for s being the symbols of members
  321.         as list = (get s 'info) do
  322.         (loop for info in list
  323.           as member = (third info)
  324.           as class = (tree-class (first info))
  325.           when (or (and (null (member-file member))
  326.                 (string= (class-file class) file))
  327.                (string= file (member-file member)))
  328.           do (browse-tags-insert-member-info info "decl.")
  329.           when (or (and (null (member-definition-file member))
  330.                 (string= (class-source-file class) file))
  331.                (string= file (member-definition-file member)))
  332.           do (browse-tags-insert-member-info info "defn."))))))
  333.  
  334. ;;;
  335. ;;; Print an info
  336. ;;; 
  337.  
  338. (defun* browse-tags-insert-member-info (info &optional (kind ""))
  339.   (insert (class-name (tree-class (first info)))
  340.       "::"
  341.       (member-name (third info)))
  342.   (indent-to 40)
  343.   (insert kind)
  344.   (indent-to 50)
  345.   (insert (case (second info)
  346.         ('tree-member-functions "member function")
  347.         ('tree-member-variables "member variable")
  348.         ('tree-static-functions "static function")
  349.         ('tree-static-variables "static variable")
  350.         ('tree-friend "friend")
  351.         ('tree-types "type"))
  352.       "\n"))
  353.  
  354. ;;;
  355. ;;; Building a list of all matches of a given string in the member obarray
  356. ;;; in the order of match quality (exact matches, then substring matches).
  357. ;;; The only question remaining is what to do with members that are defined
  358. ;;; in more than one class with the same name.
  359. ;;;
  360. ;;; We must give the user the chance of directly jumping to a (member class).
  361. ;;; On the other hand we must make it easy to traverse the list of members.
  362. ;;; I.e. we build a complete list of all (member class) pairs that we need
  363. ;;; to jump to the member.
  364. ;;; 
  365.  
  366.  
  367. ;;; end of `tags.el'.