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

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 4103f0995bd7
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/oobr/br-eif-ft.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,507 @@
+;;!emacs
+;;
+;; FILE:         br-eif-ft.el
+;; SUMMARY:      Eiffel OO-Browser class and feature functions.
+;; USAGE:        GNU Emacs Lisp Library
+;; KEYWORDS:     oop, tools
+;;
+;; AUTHOR:       Bob Weiner
+;; ORG:          Motorola Inc.
+;;
+;; ORIG-DATE:    03-Oct-90
+;; LAST-MOD:     11-May-95 at 11:24:33 by Bob Weiner
+;;
+;; Copyright (C) 1990-1995  Free Software Foundation, Inc.
+;; See the file BR-COPY for license information.
+;;
+;; This file is part of the OO-Browser.
+;;
+;; DESCRIPTION:  
+;; DESCRIP-END.
+
+;; ************************************************************************
+;; Other required Elisp libraries
+;; ************************************************************************
+
+(require 'eif-calls)
+
+;; ************************************************************************
+;; Public variables
+;; ************************************************************************
+
+(defconst eif-type-tag-separator ","
+  "String that separates a tags type from its normalized definition form.")
+
+;; ************************************************************************
+;; Public functions
+;; ************************************************************************
+
+(defun eif-feature-implementors (ftr-name)
+  "Return unsorted list of Eiffel feature tags which implement FTR-NAME."
+  (eif-feature-matches (concat "^" (regexp-quote ftr-name) "$")))
+
+(defun eif-feature-name-to-regexp (name)
+  "Converts feature NAME into a regular expression matching the feature's name tag."
+  (if (string-match (concat "^" br-feature-type-regexp " ") name)
+      (setq name (substring name (match-end 0))))
+  (format "%s%s%s %s[ \n]"
+	  eif-identifier eif-type-tag-separator br-feature-type-regexp
+	  (regexp-quote name)))
+
+(fset 'eif-feature-signature-to-name 'eif-feature-partial-name)
+
+(defun eif-feature-signature-to-regexp (signature)
+  "Given an Eiffel class or feature SIGNATURE, return regexp to match its definition."
+  (let ((regexp) class name type)
+    (setq regexp
+	  (cond ((string-match (concat eif-type-tag-separator
+				       "\\(" br-feature-type-regexp "\\) ")
+			       signature)
+		 (setq name (substring signature (match-end 0))
+		       type (string-to-char
+			     (substring
+			      signature (match-beginning 1) (match-end 1))))
+		 (cond ((memq type '(?- ?1 ?>))
+			;; routine
+			(eif-routine-to-regexp name))
+		       ((= type ?=)
+			;; attribute
+			(eif-attribute-to-regexp name))))
+		((equal 0 (string-match eif-identifier signature))
+		 ;; Assume is a class name
+		 (concat eif-class-name-before (regexp-quote signature)
+			 eif-class-name-after))))
+    (or regexp
+	(error "(eif-feature-signature-to-regexp): Invalid format, '%s'"
+	       signature))))
+
+(defun eif-feature-tree-command-p (class-or-signature)
+  "Display definition of CLASS-OR-SIGNATURE if a signature and return t, else return nil."
+  (if (br-in-browser) (br-to-view-window))
+  (br-feature-found-p (br-feature-file class-or-signature)
+		      class-or-signature))
+
+(defun eif-list-features (class &optional indent)
+  "Return sorted list of Eiffel feature names lexically defined in CLASS."
+  (let ((class-tag (concat "\n" class eif-type-tag-separator))
+	(features) start end)
+    (save-excursion
+      (set-buffer
+       (funcall br-find-file-noselect-function br-feature-tags-file))
+      (goto-char 1)
+      (if (not (search-forward class-tag nil t))
+	  nil
+	(setq start (match-beginning 0)
+	      end (if (search-forward "\^L\n" nil t)
+		      (match-beginning 0)
+		    (point-max)))
+	(goto-char start)
+	;; Feature defs can occur only within a single file.
+	(while (search-forward class-tag end t)
+	  (setq features (cons (br-feature-current) features)))
+	(eif-sort-features features)))))
+
+(defun eif-get-feature-tags (feature-file feature-list)
+  "Save Eiffel feature tags defined in FEATURE-FILE to 'br-feature-tags-file'.
+Assume FEATURE-FILE has already been read into a buffer and that
+'br-feature-tags-init' has been called.  FEATURE-LIST is the list
+of tags to save."
+  (interactive)
+  (let ((obuf (current-buffer)))
+    (set-buffer (funcall br-find-file-noselect-function br-feature-tags-file))
+    (goto-char 1)
+    ;; Delete any prior feature tags associated with feature-file
+    (if (search-forward feature-file nil 'end)
+	(progn (forward-line -1)
+	       (let ((start (point)))
+		 (search-forward "\^L" nil 'end 2)
+		 (backward-char 1)
+		 (delete-region start (point))
+		 )))
+    (if feature-list
+	(progn (insert "\^L\n" feature-file "\n")
+	       (mapcar (function (lambda (tag) (insert tag "\n")))
+		       feature-list)))
+    (set-buffer obuf)))
+
+(defun eif-scan-features-in-class (class start end)
+  "Return unordered list of Eiffel feature definitions in CLASS.
+START and END give buffer region to search."
+  (save-excursion
+    (save-restriction
+      (narrow-to-region start end)
+      (goto-char start)
+      (let ((attributes-and-routines (eif-parse-features t)))
+	(append
+	 (mapcar
+	  (function (lambda (routine)
+		      (concat class eif-type-tag-separator routine)))
+	  (cdr attributes-and-routines))
+	 (mapcar
+	  (function (lambda (attribute)
+		      (concat class eif-type-tag-separator attribute)))
+	  (car attributes-and-routines)))))))
+
+(defun eif-sort-features (feature-list)
+  (sort feature-list 'eif-feature-lessp))
+
+(defun eif-to-definition (&optional identifier)
+  "If point is within an Eiffel class or feature name, try to move to its definition.
+With optional IDENTIFIER, do the same instead for it."
+  (interactive)
+  (let ((cl (or identifier (eif-find-class-name))))
+    (cond
+     ((eif-keyword-p) nil)
+     ((br-check-for-class cl))
+     ((eif-feature cl))
+     ((progn
+	(beep)
+	(message
+	 "(OO-Browser):  Select an Eiffel identifier to move to its definition.")
+	nil))
+     )))
+
+;; ************************************************************************
+;; Private functions
+;; ************************************************************************
+
+(defun eif-export-feature-p ()
+  "Return nil unless point is within a class export clause."
+  (save-excursion
+    (let ((end (point)))
+      (beginning-of-line)
+      ;; If in a comment, return nil.
+      (if (search-forward "--" end t)
+	  nil
+	(goto-char (point-min))
+	(and (re-search-forward eif-export-key-regexp end t)
+	     (not (re-search-forward "^\\(inherit\\|feature\\)\\([ \t]\\|$\\)" end t)))))))
+
+(defun eif-feature (&optional ftr)
+  "Return nil if definition is not found for optional FTR or feature declared at point."
+  (interactive)
+  (let ((class-deferred)
+	(class)
+	(deferred-p)
+	(ftr-def-class))
+    (cond ((or ftr (and (eif-export-feature-p)
+			(setq ftr (eif-to-feature-decl))))
+	   (if (and (setq class-deferred (eif-get-class-name-from-source))
+		    (setq class (car class-deferred)
+			  deferred-p (cdr class-deferred)
+			  ftr-def-class (eif-find-ancestors-feature
+					 (list class) deferred-p ftr)))
+	       (cond ((equal (car ftr-def-class) class) t)
+		     ((equal (cdr ftr-def-class) ftr)
+		      ;; Feature inherited but not renamed.
+		      (message
+		       "Feature '%s' of class '%s' inherited from class '%s'."
+		       ftr class (car ftr-def-class)))
+		     ;; Feature inherited and renamed.
+		     (t (message "Feature '%s', class '%s' from feature '%s', class '%s'."
+				 ftr class (cdr ftr-def-class)
+				 (car ftr-def-class))
+			t))
+	     (beep)
+	     (message "(OO-Browser):  '%s' feature not found." ftr)
+	     t))
+	  ((and (not ftr) (eif-feature-def-p)))
+	  ;;
+	  ;; Later we might add the case of a feature invocation here.
+	  ;;
+	  )))
+
+(defun eif-feature-def-p ()
+  "If point is within a feature definition's name, display feature including leading comments."
+  (let ((opoint (point)))
+    (beginning-of-line)
+    (if (or (looking-at eif-routine-regexp)
+	    (looking-at eif-attribute-regexp))
+	(progn (setq opoint (match-beginning eif-feature-name-grpn))
+	       (eif-to-comments-begin)
+	       (recenter 0)
+	       (goto-char opoint)
+	       t)
+      (goto-char opoint)
+      nil)))
+
+(defun eif-feature-matches (regexp)
+  "Return an unsorted list of feature tags whose names match in part or whole to REGEXP."
+  ;; Ensure match to feature names only; also handle "^" and "$" meta-chars
+  (setq regexp
+	(concat "^\\(" eif-identifier "\\)"
+		eif-type-tag-separator
+		br-feature-type-regexp " "
+		(if (equal (substring regexp 0 1) "^")
+		    (progn (setq regexp (substring regexp 1)) nil)
+		  (concat "[" eif-identifier-chars "]*"))
+		(if (equal (substring regexp -1) "$")
+		    (substring regexp 0 -1)
+		  (concat regexp "[" eif-identifier-chars "]*"))
+		"[ \t\n\r]"))
+  (save-excursion
+    (set-buffer (funcall br-find-file-noselect-function br-feature-tags-file))
+    (goto-char 1)
+    (let ((features) start end)
+      (if (not (re-search-forward regexp nil t))
+	  nil
+	(setq start (match-beginning 0)
+	      end (if (search-forward "\^L\n" nil t)
+		      (match-beginning 0)
+		    (point-max)))
+	(goto-char start)
+	;; Feature defs can occur only within a single file.
+	(while (re-search-forward regexp end t)
+	  (backward-char) ;; Might have moved past newline.
+	  (setq features (cons (br-feature-current) features))))
+      features)))
+
+(defun eif-feature-lessp (feature1 feature2)
+  (string-lessp (eif-feature-partial-name feature1)
+		(eif-feature-partial-name feature2)))
+
+(defun eif-feature-partial-name (signature &optional with-class for-display)
+  "Extract the feature name without its class name from feature SIGNATURE.
+If optional WITH-CLASS is non-nil, class name and 'eif-type-tag-separator'
+are prepended to the name returned.  If optional FOR-DISPLAY is non-nil, a
+feature type character is prepended to the name for display in a browser
+listing."
+  (if (string-match (concat eif-type-tag-separator
+			    "\\(" br-feature-type-regexp " \\)")
+		    signature)
+      (let ((class (substring signature 0 (match-beginning 0)))
+	    (name (substring signature (match-end 0))))
+	(cond ((and with-class for-display)
+	       signature)
+	      (with-class
+	       (concat class eif-type-tag-separator name))
+	      (for-display
+	       (substring signature (match-beginning 1)))
+	      (t name)))
+    signature))
+
+(defun eif-feature-tag-class (element-tag)
+  "Extract the class name from ELEMENT-TAG."
+  (if (string-match eif-type-tag-separator element-tag)
+      (substring element-tag 0 (match-beginning 0))
+    ""))
+
+(defun eif-find-ancestors-feature (class-list deferred-class ftr)
+  (let* ((classes class-list)
+	 (cl)
+	 (file)
+	 (found-ftr))
+    (if (null class-list)
+	nil
+      (while (and (not found-ftr) classes)
+	(setq cl (car classes)
+	      file (br-class-path cl))
+	(and file (setq found-ftr
+			(br-feature-found-p file ftr deferred-class)))
+	;; If found-ftr is a cons cell, then only one parent class need
+	;; be searched to look for ftr.
+	(if (consp found-ftr)
+	    (setq class-list (list (car found-ftr))
+		  ftr (cdr found-ftr)))
+	(setq classes (cdr classes)))
+      (cond ((consp found-ftr)
+	     (eif-find-ancestors-feature class-list deferred-class ftr))
+	    ((null found-ftr)
+	     (eif-find-ancestors-feature 
+	      (apply 'append (mapcar (function
+				       (lambda (cl) (br-get-parents cl)))
+				     class-list))
+	      deferred-class
+	      ftr))
+	    (t (cons cl ftr))))))
+
+;; Prefixed with 'eiffel' rather than 'eif' since works as a standalone
+;; feature in buffers whose major mode is 'eiffel-mode'.  It is used by the
+;; browser but may also be used standalone.
+;;
+(defun eiffel-find-feature (feature-name)
+  "Move point to start of feature named FEATURE-NAME in current buffer.
+Display feature including all preceding comments at the top of the window.
+Move point and return non-nil iff FEATURE-NAME is found."
+  (interactive "sFeature to find: ")
+  (cond ((eif-locate-feature
+	  feature-name (eif-routine-to-regexp feature-name)))
+	((eif-to-attribute feature-name)
+	 (let ((opoint (point)))
+	   (eif-to-comments-begin)
+	   (recenter 0)
+	   (goto-char opoint)
+	   (back-to-indentation)
+	   t))))
+
+(defun eif-find-class-name ()
+  "Return class name that point is within, else nil."
+  (if (= (point) (point-max)) (skip-chars-backward " \t\n"))
+  (save-excursion
+    (skip-chars-forward " \t")
+    (skip-chars-backward eif-identifier-chars)
+    (skip-chars-backward " \t\n")
+    (backward-char 1)
+    (and (looking-at eif-class-name-pat)
+	 (eif-set-case
+	  (buffer-substring (match-beginning 2)
+			    (match-end 2))))))
+
+(defun eif-find-feature (feature-name)
+  "With point selecting a class in a listing buffer, move point to definition of FEATURE-NAME in viewer window.
+Move point and return non-nil iff FEATURE-NAME is found."
+  (interactive "sFeature to find: ")
+  ;; If selected class is displayed, don't go to start of class
+  (if (equal (br-class-path (br-find-class-name))
+	     (progn
+	       (br-to-from-viewer)
+	       (expand-file-name buffer-file-name)))
+      nil
+    (br-edit))
+  (if (eiffel-find-feature feature-name)
+      (progn (recenter 0)
+	     t)
+    (br-to-from-viewer)
+    (and (interactive-p)
+	 (progn
+	   (beep)
+	   (message "(OO-Browser):  No '%s' feature found." feature-name)))))
+
+(defun eif-feature-locate-p (feature-tag)
+  (let (start class)
+    (if (string-match (concat "\\`[^\]\[]+" eif-type-tag-separator)
+		      feature-tag)
+	;; First move to the proper class implementation, so that if two
+	;; classes in the same file have the same feature signature, we still
+	;; end up at the right one.
+	(progn
+	  (setq class (substring feature-tag 0 (1- (match-end 0))))
+	  (re-search-forward
+	   (concat eif-class-name-before (regexp-quote class)
+		   eif-class-name-after)
+	   nil t)))
+    (if (not (re-search-forward
+	      (eif-feature-signature-to-regexp feature-tag) nil t))
+	nil
+      (setq start (match-beginning 0))
+      (goto-char start)
+      (skip-chars-forward " \t\n")
+      (eif-to-comments-begin)
+      (recenter 0)
+      (goto-char start)
+      t)))
+
+(defun eif-keyword-p ()
+  "Return t if point is within an Eiffel keyword, else nil."
+  (if (= (point) (point-max)) (skip-chars-backward " \t\n"))
+  (save-excursion
+    (skip-chars-forward " \t")
+    (skip-chars-backward eif-identifier-chars)
+    (and (looking-at eif-identifier)
+	 (br-member-sorted-strings (buffer-substring (match-beginning 0)
+						     (match-end 0))
+				   eif-reserved-words))))
+
+(defun eif-locate-feature (ftr ftr-pat)
+  (let ((opoint (point)))
+    (goto-char (point-min))
+    (if (and (re-search-forward "^feature\\([ \t]\\|$\\)" nil t)
+	     (re-search-forward ftr-pat nil t))
+	(progn (goto-char (match-beginning eif-feature-name-grpn))
+	       (setq opoint (point))
+	       (eif-to-comments-begin)
+	       (recenter 0)
+	       (goto-char opoint)
+	       t)
+      (goto-char opoint)
+      (and (interactive-p) (error (format "Feature '%s' not found."
+					  ftr))))))
+
+(defun eif-renamed-feature-p (ftr)
+  (goto-char (point-min))
+  (let ((rename-regexp "[ \t\n]+rename[ \t\n]")
+	(rename-match
+	 (concat eif-identifier "[ \t\n]+as[ \t\n]+" ftr "[,; \t\n]"))
+	(prev-feature-nm)
+	(prev-class)
+	(parents))
+    (while (and (setq prev-feature-nm
+		      (and (re-search-forward rename-regexp nil t)
+			   (re-search-forward rename-match nil t)))
+		(setq prev-feature-nm
+		      (buffer-substring (match-beginning 1) (match-end 1))
+		      prev-class (match-beginning 0))
+		(progn (backward-char 1)
+		       (eif-in-comment-p))))
+    (if prev-feature-nm
+	(progn (goto-char prev-class)
+	       (setq parents (eif-get-parents-from-source buffer-file-name))
+	       (if (re-search-backward (concat
+					"[^[][ \t\n]+\\("
+					(mapconcat
+					  (function (lambda (cl)
+						      (eif-set-case-type cl)))
+					  parents
+					  "\\|")
+					"\\)")
+				       nil t)
+		   (progn (setq prev-class (eif-set-case (buffer-substring
+							  (match-beginning 1)
+							  (match-end 1))))
+			  (cons prev-class prev-feature-nm))
+		 (beep)
+		 (message
+		  "(OO-Browser):  Internal error - no class associated with rename clause."))))))
+
+(defun eif-to-feature-decl ()
+  (let ((end))
+    (while (and (progn (skip-chars-backward "^, \t\n")
+		       (and (not (= (preceding-char) ?,))
+			    (not (looking-at "export[ \t\n]+"))))
+		(progn (skip-chars-backward " \t\n")
+		       (setq end (point))
+		       (beginning-of-line)
+		       (if (search-forward "--" end t)
+			   (progn (goto-char end)
+				  (skip-chars-forward " \t\n")
+				  nil)
+			 (goto-char end)
+			 t)))))
+  (if (looking-at "export[ \t\n]+")
+      (goto-char (match-end 0))
+    (skip-chars-forward " \t\n"))
+  (if (looking-at eif-feature-name)
+      (buffer-substring (match-beginning 0) (match-end 0))))
+
+
+;; ************************************************************************
+;; Private variables
+;; ************************************************************************
+
+(defconst eif-feature-name
+  (concat 
+   "\\("
+   "\\(prefix[ \t]+\"\\(not\\|\\+\\|-\\)\"\\)"
+   "\\|infix[ \t]+\"\\(div\\|mod\\|^\\|<=?\\|>=?\\|\+\\|-\\|\\*\\|/"
+                   "\\|and then\\|and\\|or else\\|or\\|xor\\|implies\\)"
+   "\\|" eif-identifier "\\)")
+  "Regexp matching any Eiffel feature name.
+Will also match class names and keywords, so tests for these should precede
+use of this expression.")
+
+(defconst eif-export-key-regexp
+  "\\(^[ \t]*\\|[ \t]+\\)export[ \t\n]+"
+  "Regexp matching the Eiffel export keyword in context.")
+
+(defconst eif-class-repeat (concat "repeat[ \t]+" eif-identifier)
+  "Match to an Eiffel 'repeat <class>' phrase.  Grouping 1 is class name.")
+
+(defconst eif-exported-feature
+  (concat "\\(,\\|export[ \t\n]+\\(--.*[ \t\n]+\\)*\\)"
+	  eif-feature-name "\\([ \t]*{[^}]+}\\)?"
+	  "\\([ \t]*[\n,]\\|[ \t]+--\\)")
+  "Regexp to match to a feature declaration in an export clause.
+  Exclude 'repeat <class>' phrases.  Feature name is grouping 3.")
+
+
+(provide 'br-eif-ft)