diff lisp/energize/energize-visit-use.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/energize/energize-visit-use.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,682 @@
+;;; -*- Mode:Emacs-Lisp -*-
+;;; Copyright © 1992-1993 by Lucid, Inc.  All Rights Reserved.
+
+;; Load this file and ^X-\ (back-slash) gets bound to a
+;; command that visit all the places where a language element is used.  
+;; It can be started from the Le-Browser buffer of that language element
+;; or from a toplevel from defining the element.
+;; 
+;; The visit happens as follows:
+;;   - the 1st ^X-\ jumps to the beginning of the definition of the 1sr user.
+;;   - then search-forward inside the first user for the name of the
+;;     language element that was in the le-browser buffer.
+;;   - the 2rd one searches again etc.. until no more match is found inside
+;;     the body of the 1 user.
+;;   - after the last match is seen the next user of the LE is visited 
+;;     the same way.
+;; 
+;; If you want to start revisiting while a previous visit is not terminated do
+;; ^U-^X-\ in another (or the same) le-browser buffer.
+;; 
+;; If you position the point on a User in the LE Browser buffer before doing
+;; ^X-\, that use will become the next one shown.
+;;
+;; energize-next-use-previous, ^U-1-^X-\, backs up one use
+;;
+;; It's quite crude and the UI could be improved in different ways.  
+;; What about:
+;;   - Allow for stacking of visits so that you can recursively 
+;;     visit another LE while one is being visited.
+;;   - Highlight all the occurences of the LE-name inside the body of the user.
+;;   - Deal with *alternatives*
+
+(defvar energize-next-use-show-le-browser t
+  "*If t energize-next-use-command will show both the next use and the le-browser buffer")
+
+(defvar energize-next-use-search-by-file nil ;; default is off cause on is slow
+  "*If t energize-next-use-command will show all uses in a file before going to
+the next file")
+
+(defvar energize-next-use-command-name ())
+(defvar energize-next-use-label ())
+(defvar energize-next-use-name ())
+(defvar energize-next-use-from-mark ())
+(defvar energize-next-use-source-start-mark ())
+(defvar energize-next-use-source-end-mark ())
+(defvar energize-next-use-source-current-mark ())
+(defvar energize-next-use-current-file ())
+(defvar energize-next-use-file-began-mark ())
+(defvar energize-next-use-from-extent-cache ())
+(defvar energize-next-use-history ())
+(defvar energize-next-use-history-pointer ())
+(defvar energize-next-use-marker-heap ())
+
+;; remember markers we allocate so we can clean them up
+(defun energize-next-use-make-a-marker ()
+  (let ((m (make-marker)))
+    (setq energize-next-use-marker-heap
+	  (cons m energize-next-use-marker-heap))
+    m))
+
+(defun energize-next-use-copy-a-marker (mark)
+  (let ((m (copy-marker mark)))
+    (setq energize-next-use-marker-heap
+	  (cons m energize-next-use-marker-heap))
+    m))
+
+(defun energize-next-use-cleanup-markers ()
+  (while energize-next-use-marker-heap
+    (set-marker (car energize-next-use-marker-heap) nil)
+    (setq energize-next-use-marker-heap (cdr energize-next-use-marker-heap))))
+
+(defun extent-after (pos)
+  (let ((extent (next-extent (current-buffer))))
+    (while (and extent (< (extent-start-position extent) pos))
+      (setq extent (next-extent extent)))
+    extent))
+
+(defun energize-next-use-start-pos ()
+  (save-excursion
+    (goto-char (point-min))
+    (if (search-forward energize-next-use-label () t)
+	(point)
+      ())))
+
+;; see if we are in the LE Browser buffer of the object we are looking at
+;; uses of.  If so, capture the position as a possible place to find the
+;; next user, rather than take the first or next in sequence.
+(defun energize-next-use-start-pos-offset ()
+  ;; energize-next-use-from-mark is NULL iff this is called from
+  ;; energize-next-use-init - in that case assume that if we are in a LEB
+  ;; it is the one we will be using
+  (if (if (and energize-next-use-from-mark
+	       (marker-buffer energize-next-use-from-mark))
+	  (eq (current-buffer) (marker-buffer energize-next-use-from-mark))
+	(eq major-mode 'energize-browser-mode))
+      (save-excursion
+	(beginning-of-line)
+	(point))))
+
+(defun energize-next-use-get-name (name)
+  (let ((colon (- (length name) 1)))
+    (while (and (> colon 0) (not (eq (aref name colon) ?:)))
+      (setq colon (- colon 1)))
+    (if (= (aref name colon) ?:)
+	(substring name (+ colon 1))
+      name)))
+
+;; cache the extents (and later the results of following them to the
+;; source buffer) to avoid calling the server too many times
+;; returns nil if the extent does not have a "source" menu option
+;; returns the cache entry otherwise
+(defun energize-next-use-valid-from-extent-p (buff extent)
+  (let ((cache-entry (assoc extent energize-next-use-from-extent-cache)))
+    (if (not cache-entry)
+	(if (energize-list-menu buff extent ()
+				energize-next-use-command-name)
+	    (setq energize-next-use-from-extent-cache
+		  (cons (setq cache-entry (list extent))
+			energize-next-use-from-extent-cache))))
+    cache-entry))
+
+;; decide what use to look at next.  current is nil if called from
+;; energize-next-use-next, else it is the presumed first extent for
+;; energize-next-use-from-mark passed from energize-next-use-init
+;; wanted-pos is the position of the cursor in the LE Browser of the
+;; object being looked for, or nil if the cursor is not in that buffer
+;; if wanted-pos is non-nil it means we will consider restarting the
+;; search at the user indicated by that position.  Otherwise, we just
+;; return current.  If this is nil too, it means just continue searching
+;; as before.  If not, it means continue initializing as before.
+;;
+;; returns an extent to be used to set energize-next-use-from-mark or nil
+(defun energize-next-use-adjust-for-start-offset (current wanted-pos)
+  ;; assumes we're in the LE browser buffer (we may not have been when
+  ;; the value of wanted-pos was determined)
+  (if wanted-pos
+      (let ((end-pos (energize-next-use-end-pos))
+	    ;; ok will be set to the first extent found that is actually
+	    ;; a user
+	    (ok nil)
+	    (try (or current (extent-after (energize-next-use-start-pos)))))
+	;; loop will exit if an actual user extent is found that follows
+	;; the wanted-pos - if we go out of bounds, try is set to nil
+	;; else it is the desired extent on exit from the loop
+	(while (and try
+		    (or (< (extent-start-position try) end-pos)
+			(setq try nil))
+		    (not
+		     (and
+		      ;; try is a valid user extent if this menu exists
+		      (energize-next-use-valid-from-extent-p
+		       (current-buffer) try)
+		      ;; but it might be before wanted-pos,
+		      (or (> (extent-end-position try) wanted-pos)
+			  ;; in which case remember it as ok unless
+			  ;; ok was already set 
+			  (and (setq ok (or ok try)) nil)))))
+	  (setq try (next-extent try)))
+	;; return try or if we were called from init, return ok
+	;; else return nil to mean continue searching in current sequence
+	(or try (and current ok)))
+    current))
+
+(defun energize-next-use-set-marks (extent buffer)
+  (if (not energize-next-use-from-mark)
+      (setq energize-next-use-from-mark (make-marker)))
+  (set-marker energize-next-use-from-mark 
+	      (extent-start-position extent) buffer))
+
+(defun energize-next-use-init (command label)
+  ;; start-offset is the position of point if it is in the LEB buffer
+  (let ((start-offset (energize-next-use-start-pos-offset)))
+    (if (not (eq major-mode 'energize-browser-mode))
+	(if (energize-list-menu (current-buffer)
+				(energize-extent-at (point)) ()
+				"browse_le")
+	    (energize-execute-command "browse_le"
+				      (energize-extent-at (point)) () t)
+	  (error
+       "command undefined unless in a LE buffer or at a LE definition form")))
+    (setq energize-next-use-command-name command)
+    (setq energize-next-use-label label)
+    (setq energize-next-use-current-file nil)
+    (setq energize-next-use-from-extent-cache nil)
+    (setq energize-next-use-history nil)
+    (setq energize-next-use-history-pointer nil)
+    (let* ((pos (energize-next-use-start-pos))
+	   (extent (and pos
+			;; start at the beginning, unless start-pos says
+			;; to start further along
+			(energize-next-use-adjust-for-start-offset
+			 (extent-after pos) start-offset))))
+      (if (null extent)
+	  (error "no uses")
+	(energize-next-use-set-marks extent (current-buffer))
+	(setq energize-next-use-name
+              (save-excursion
+                (let ((s (energize-next-use-get-name (buffer-name))))
+                  (if (let ((l (length s)))
+                        (while (not
+                                (and (> l 0)
+                                     (progn
+                                       (beginning-of-buffer)
+                                       (search-forward
+                                        (substring s 0 l) nil t))))
+                          (setq l (- l 1)))
+                        (> l 0))
+                      (let (pos)
+                        (backward-sexp)
+                        (setq pos (point))
+                        (forward-sexp)
+                        (buffer-substring pos (point)))
+                    s))))))
+    (energize-next-use-show-both)))
+
+(defun energize-next-use-end-pos ()
+  (save-excursion
+    (goto-char (point-min))
+    (search-forward energize-next-use-label)
+    (re-search-forward "^ *[A-Za-z ]*: " () 'end)
+    (point)))
+
+;; return the source-entry portion of the extent cache, looking it up
+;; if necessary by doing energize-execute-command "source" on the extent
+;; source-entry portion is list of
+;; a) point marker after executing the "source" command
+;; b) a flag saying we've actually visited this use, not just looked it up
+;; c) marker for the window-start after executing the "source" command
+(defun energize-next-use-from-extent-source-info (from-extent-entry)
+  (let ((extent (car from-extent-entry))
+	(source-entry (cdr from-extent-entry)))
+    (if (not source-entry)
+	(save-window-excursion
+	  (save-excursion
+	    (let ((pre-display-buffer-function nil)) ;; hack for multi-frame
+	      (energize-execute-command
+	       energize-next-use-command-name extent () t))
+	    (setcdr from-extent-entry
+		    (setq source-entry
+			  (list (energize-next-use-copy-a-marker
+				 (point-marker))
+				nil
+				(set-marker
+				 (energize-next-use-make-a-marker)
+				 (window-start))))))))
+    source-entry))
+
+(defun energize-next-use-from-extent-source-mark (source-entry)
+  (car source-entry))
+
+(defun energize-next-use-from-extent-source-start-mark (source-entry)
+  (car (cdr (cdr source-entry))))
+
+(defun energize-next-use-from-extent-source-seen-p (source-entry)
+  (car (cdr source-entry)))
+
+(defun energize-next-use-from-extent-set-source-seen-p (source-entry)
+  (setcar (cdr source-entry) t))
+
+;; goto the next user - or if advance is nil try to re-establish the
+;; extent for the current one
+(defun energize-next-use-next-to-extent (advance)
+  (let ((buff (marker-buffer energize-next-use-from-mark))
+	(from-pos (marker-position energize-next-use-from-mark)))
+    (and buff from-pos
+	 (let ((result nil))
+	   (set-buffer buff)
+	   (let ((end-pos (energize-next-use-end-pos))
+		 (extent (or (energize-extent-at from-pos)
+			     (setq advance nil)
+			     (extent-after from-pos))))
+	     (if (and advance energize-next-use-search-by-file)
+		 (if (not energize-next-use-current-file)
+		     ;; if searching by file but dont have a current file,
+		     ;; set current file to current source buffer
+		     (if (setq energize-next-use-current-file
+			       (and energize-next-use-source-start-mark
+				    (marker-buffer
+				     energize-next-use-source-start-mark)))
+			 (progn
+			   (if (not energize-next-use-file-began-mark)
+			       (setq energize-next-use-file-began-mark
+				     (make-marker)))
+			   (set-marker energize-next-use-file-began-mark
+				       from-pos buff))))
+	       (setq energize-next-use-current-file nil))
+	     (if advance
+		 (setq extent (next-extent extent)))
+	     (if energize-next-use-search-by-file
+		 (message "Searching for next use in current file...")
+	       (message "Searching for next use..."))
+	     ;; validate the extent
+	     (while extent
+	       (while
+		   (and
+		    extent
+		    ;; if extent is beyond the end of the "used by" zone
+		    ;; set it to nil to terminate loop
+		    (or (< (extent-start-position extent) end-pos)
+			(setq extent nil))
+		    (not
+		     (let ((from-extent-entry
+			    (energize-next-use-valid-from-extent-p
+			     buff extent)))
+		       (and
+			;; true if the extent has a "source" menu option
+			from-extent-entry
+			(or
+			 (not energize-next-use-search-by-file)
+			 ;; see if this is extent points to the current file
+			 ;; or a file we haven't looked at yet, if not current
+			 ;; file is set now
+			 (let* ((source-entry
+				 (energize-next-use-from-extent-source-info
+				  from-extent-entry))
+				(source-marker
+				 (energize-next-use-from-extent-source-mark
+				  source-entry))
+				(buff (marker-buffer source-marker)))
+			   (if energize-next-use-current-file
+			       (eq buff energize-next-use-current-file)
+			     (not
+			      (energize-next-use-from-extent-source-seen-p
+			       source-entry)))))))))
+		 (setq extent (next-extent extent)))
+	       (if extent
+		   ;; we found one we can use - remember it but set extent
+		   ;; to nil to terminate the loop
+		   (progn
+		     ;; convert the extent to a mark for future reference
+		     (energize-next-use-set-marks extent buff)
+		     ;; terminate the loop
+		     (setq extent nil)
+		     ;; saw we succeeded in finding something
+		     (setq result t))
+		 ;; we didn't find anything valid - if we were looking in
+		 ;; the current file, restart the loop now looking for
+		 ;; anything we haven't used yet, to make it the current file
+		 ;; if we weren't looking in the current file then we really
+		 ;; have failed to find anything left, so return nil
+		 (if energize-next-use-current-file
+		     (progn
+		       (setq energize-next-use-current-file nil)
+		       (setq extent
+			     ;; restart loop from were we started looking at
+			     ;; the current file
+			     (let ((pos (marker-position
+					 energize-next-use-file-began-mark)))
+			       (and pos
+				    (or (energize-extent-at pos)
+					(extent-after pos)))))))))
+	     (if energize-next-use-search-by-file
+		 (message "Searching for next use in current file...done")
+	       (message "Searching for next use...done"))
+	     result)))))
+
+(defun energize-next-use-show-from (&optional mark)
+  (setq mark (or mark energize-next-use-from-mark))
+  (if energize-next-use-show-le-browser
+      ;; position the browser to the line of the current user
+      (let ((buff (marker-buffer mark))
+	    (from-pos (marker-position mark)))
+	(and buff from-pos
+	     (progn
+	       ;; don't worry about pre-display-buffer-function here;
+	       ;; we actually want a new frame to be created when
+	       ;; displaying the Browser buffer.
+	       (pop-to-buffer buff)
+	       (goto-char from-pos)
+	       (beginning-of-line)
+	       (set-window-start (selected-window) (point)))))
+    (message "next use of %s" energize-next-use-name)))
+
+(defun energize-next-use-show-to ()
+  (let* ((buff (marker-buffer energize-next-use-from-mark))
+	 (from-pos (marker-position energize-next-use-from-mark))
+	 (from-extent (and buff from-pos (energize-extent-at from-pos buff)))
+	 (from-extent-entry
+	  ;; get the cache entry for the current user extent in the browser
+	  (energize-next-use-valid-from-extent-p buff from-extent))
+	 (source-extent nil))
+    (and
+     from-extent-entry
+     (let* ((source-entry
+	     (energize-next-use-from-extent-source-info
+	      from-extent-entry))
+	    (source-marker
+	     (energize-next-use-from-extent-source-mark
+	      source-entry))
+	    (source-buffer (marker-buffer source-marker))
+	    (source-pos (marker-position source-marker))
+	    ;; if we're allowed to split frames, call pop-to-buffer
+	    ;; with no pre-display-buffer-function, so that the current
+	    ;; frame (the frame of the Browser buffer) is used.  If
+	    ;; we don't split frames, then use the p-d-b-f, so that a
+	    ;; new frame can be created for this file.
+	    (pre-display-buffer-function
+	     (if energize-split-screens-p
+		 nil
+	       pre-display-buffer-function)))
+       (if source-pos
+	   (progn
+	     ;; position the source window as if we had just executed the
+	     ;; command "source" on the from-extent in the browser
+	     (pop-to-buffer source-buffer)
+	     (set-window-start
+	      (selected-window)
+	      (marker-position
+	       (energize-next-use-from-extent-source-start-mark source-entry))
+	      t)
+	     (goto-char source-marker)
+	     ;; note that we have actually visited this use
+	     (energize-next-use-from-extent-set-source-seen-p source-entry)
+	     (setq source-extent (energize-extent-at (point))))))
+     (let ((start-pos (extent-start-position source-extent)))
+       ;; convert positions to markers
+       (if (not energize-next-use-source-start-mark)
+	   (setq energize-next-use-source-start-mark (make-marker)))
+       (if (not energize-next-use-source-end-mark)
+	   (setq energize-next-use-source-end-mark (make-marker)))
+       (if (not energize-next-use-source-current-mark)
+	   (setq energize-next-use-source-current-mark (make-marker)))
+       (set-marker energize-next-use-source-start-mark start-pos)
+       (set-marker energize-next-use-source-current-mark start-pos)
+       (set-marker energize-next-use-source-end-mark
+		   (extent-end-position source-extent))))))
+
+(defun energize-next-use-search-for-name ()
+  (if (let ((case-fold-search nil)
+	    (found nil)
+	    (end-pos (marker-position energize-next-use-source-end-mark)))
+	(if end-pos
+	    (progn
+	      (buffer-syntactic-context-flush-cache)
+	      ;; case sensitive exact search for token not in comment or string
+	      (while
+		  (and
+		   (setq found
+			 (re-search-forward
+			  (concat "[^A-Za-z0-9_]"
+				  (regexp-quote energize-next-use-name)
+				  "[^A-Za-z0-9_]")
+			  end-pos t))
+		   (save-excursion (buffer-syntactic-context))))))
+	found)
+      (progn
+	;; allow for the delimiter
+	(backward-char 1)
+	t)
+    nil))
+
+
+;; say this if search can't find the thing that is being used
+(defun energize-next-use-not-lexically-apparent ()
+  (message "next use of %s is here, but not lexically apparent"
+	   energize-next-use-name))
+
+;; called when visiting a new users, not a new use within that user
+(defun energize-next-use-show-both ()
+  ;; reposition the browser to the line showing the user
+  (energize-next-use-show-from)
+  ;; position the source to the user definition form
+  (energize-next-use-show-to)
+  ;; instead of stopping at the beginning of the user form,
+  ;; go immediately to the first use found if it is lexically apparent
+  (if (and
+       energize-next-use-source-start-mark
+       (let ((buff (marker-buffer energize-next-use-source-start-mark))
+	     (current-pos
+	      (marker-position energize-next-use-source-current-mark))
+	     (end-pos (marker-position energize-next-use-source-end-mark)))
+	 (if (and buff current-pos end-pos)
+	     (save-excursion
+	       (set-buffer buff)
+	       (and (< current-pos end-pos)
+		    (progn
+		      (goto-char current-pos)
+		      (energize-next-use-search-for-name)))))))
+      ;; an apparent use is there, so proceed as if going to the next use
+      ;; within the user
+      (energize-next-use-next)
+    ;; no use is apparent, so print a message
+    (energize-next-use-not-lexically-apparent)
+    ;; record the history entry for a non-apparent use - it is distinguished
+    ;; by having no point position from which to make a region to highlight
+    (setq energize-next-use-history
+	  (cons (list (energize-next-use-copy-a-marker
+		       energize-next-use-from-mark)
+		      (energize-next-use-copy-a-marker
+		       energize-next-use-source-current-mark)
+		      (set-marker (energize-next-use-make-a-marker)
+				  (window-start)))
+		energize-next-use-history))
+    (setq energize-next-use-history-pointer
+	  energize-next-use-history)))
+
+;; show the next use within the current user - if no more are found
+;; go to the next user
+(defun energize-next-use-next ()
+  ;; new-start will get set to an extent for a user at which to
+  ;; reposition the visit, iff the cursor is in the LEB buffer
+  ;; of the usee at a valid position in the list of users.
+  (let ((new-start (energize-next-use-adjust-for-start-offset
+		    nil (energize-next-use-start-pos-offset)))
+	(buff (marker-buffer energize-next-use-source-start-mark))
+	(current-pos (marker-position energize-next-use-source-current-mark))
+	(end-pos (marker-position energize-next-use-source-end-mark)))
+    (if (and buff (not (eq buff (current-buffer))))
+	(set-buffer buff))
+    ;; new-start means we are repositioning due to the point being in the
+    ;; user list in the browser.  In this case we will act as if we are
+    ;; at the end of the old user
+    (if new-start
+	(energize-next-use-set-marks
+	 new-start
+	 (marker-buffer energize-next-use-from-mark)))
+    (cond ((and buff current-pos end-pos
+		(not new-start) ;; don't stay in current user if repositioning
+		(< current-pos end-pos)
+		(progn
+		  (goto-char current-pos)
+		  (energize-next-use-search-for-name)))
+	   ;; the 'still in the same user' case
+	   (set-marker energize-next-use-source-current-mark (point))
+	   ;; redisplay the browser in case it got covered up
+	   (energize-next-use-show-from)
+	   ;; We know that we're not changing files, so there won't be any
+	   ;; need to create a new frame; it would be nice if we reselected
+	   ;; the appropriate frame instead of just using the current frame,
+	   ;; but that's more work, and this will very rarely do the wrong
+	   ;; thing.
+	   (let ((pre-display-buffer-function 
+		  (if energize-split-screens-p
+		      nil
+		    pre-display-buffer-function)))
+	     ;; display the source buffer
+	     (pop-to-buffer buff)
+	     ;; had to do this because window-start overrides point
+	     ;; in terms of deciding what part of the window to show
+	     ;; this seems like a possible bug
+	     (set-window-buffer (selected-window) buff)
+	     (push-mark (point) t)
+	     (backward-char (length energize-next-use-name))
+	     (if zmacs-regions
+		 (zmacs-activate-region))
+	     ;; record the visit in the history - this entry has a point
+	     ;; marker, distinguishing it from a non-lexically apparent one
+	     (setq energize-next-use-history
+		   (cons (list
+			  (energize-next-use-copy-a-marker
+			   energize-next-use-from-mark)
+			  (energize-next-use-copy-a-marker
+			   energize-next-use-source-current-mark)
+			  (set-marker (energize-next-use-make-a-marker)
+				      (window-start))
+			  (set-marker (energize-next-use-make-a-marker)
+				      (point)))
+			 energize-next-use-history))
+	     (setq energize-next-use-history-pointer
+		   energize-next-use-history)))
+	  ((or new-start
+	       (energize-next-use-next-to-extent
+		(and buff current-pos end-pos)))
+	   ;; the 'moved to a new user or repositioned to one' case
+	   (energize-next-use-show-both))
+	  (t
+	   ;; no more users
+	   (energize-next-use-terminate)
+	   (error "no more")))))
+
+
+(defun energize-next-use-terminate ()
+  (setq energize-next-use-command-name ())
+  (setq energize-next-use-label ())
+  (setq energize-next-use-name ())
+  (if energize-next-use-from-mark
+      (set-marker energize-next-use-from-mark nil))
+  (if energize-next-use-source-start-mark
+      (set-marker energize-next-use-source-start-mark nil))
+  (if energize-next-use-source-current-mark
+      (set-marker energize-next-use-source-current-mark nil))
+  (if energize-next-use-source-end-mark
+      (set-marker energize-next-use-source-end-mark nil))
+  (if energize-next-use-file-began-mark
+      (set-marker energize-next-use-file-began-mark nil))
+  (energize-next-use-cleanup-markers))
+
+;; get next use from history list if we have regressed or are regressing
+;; if history-pointer == history then we are in the present
+;; to go back set history-pointer to cdr history-pointer
+;; to go forward set history-pointer to the last cell on history prior to
+;; the current value of history-pointer
+;; a history entry is just redisplay info for the browser and source buffers
+(defun energize-next-use-from-history (backward)
+  (if backward
+      (setq energize-next-use-history-pointer
+	    (cdr energize-next-use-history-pointer))
+    (let ((temp energize-next-use-history-pointer))
+      (setq energize-next-use-history-pointer
+	    energize-next-use-history)
+      (while (not (eq (cdr energize-next-use-history-pointer) temp))
+	(setq energize-next-use-history-pointer
+	      (cdr energize-next-use-history-pointer)))))
+  (let* ((history-entry (car energize-next-use-history-pointer))
+	 (from-mark (car history-entry))
+	 (source-mark (car (cdr history-entry)))
+	 (source-start-mark (car (cdr (cdr history-entry))))
+	 (source-point-mark (car (cdr (cdr (cdr history-entry))))))
+    (energize-next-use-show-from from-mark)
+    (if (and (marker-position source-mark)
+	     (marker-position source-start-mark))
+	(progn
+	  (pop-to-buffer (marker-buffer source-mark))
+	  (set-window-start
+	   (selected-window) (marker-position source-start-mark) t)
+	  (goto-char source-mark)
+	  (if (and source-point-mark (marker-position source-point-mark))
+	      (progn
+		(push-mark (point) t)
+		(goto-char source-point-mark)
+		(if zmacs-regions
+		    (zmacs-activate-region)))
+	    (energize-next-use-not-lexically-apparent))))))
+
+(defun energize-next-use-go (label command)
+  (if (and energize-next-use-from-mark
+	   (marker-position energize-next-use-from-mark))
+      (if (eq energize-next-use-history-pointer
+	      energize-next-use-history)
+	  ;; normal case
+	  (energize-next-use-next)
+	;; in the depths of history but going forward
+	(energize-next-use-from-history nil))
+    ;; beginning of time
+    (energize-next-use-init label command)))
+
+(defun energize-next-use-previous ()
+  (if (cdr energize-next-use-history-pointer)
+      ;; going back in time
+      (energize-next-use-from-history t)
+    ;; trying to go beyond the pale
+    (error "no prior use")))
+	
+(defun energize-next-use-command (arg)
+  "Show the next place where the current language element is used.
+The current language element is the one that point is on for source buffers
+or the element displayed in a language element browser buffer.
+Repeated calls to this functions visit all the callers in sequence.  
+
+With prefix arg = 1, back up to the last use.
+
+With prefix arg > 1, start a visit from scratch on the current language 
+element.
+
+If the point is on a particular user in the language element browser, the
+search will be (re)started in that user.
+
+The variable energize-next-use-show-le-browser controls if this command should
+display both the language element browser buffer and the next user or just the
+next user of the current language element.
+
+The variable energize-next-use-search-by-file controls whether all uses within
+a given file should be shown in sequence.  If the value is nil, the uses
+are shown in the order in which they appear in the LE Browser.  If the
+value is non-nil, the all uses in a given file are shown before proceeding to
+the next use in another file."
+  (interactive "P")
+  (if (eq arg 1)
+      (energize-next-use-previous)
+    (if arg
+	(energize-next-use-terminate))
+    (energize-next-use-go "source" "Used By:")))
+
+(define-key global-map '[(control x) \\] 'energize-next-use-command)
+
+(defun energize-next-use-start ()
+  "Start visiting the uses of a language element.
+If executed in a LE Browser buffer visit the uses of the language element
+in the buffer.  In a source buffer visit the uses of the language element
+at (point)"
+  (interactive)
+  (energize-next-use-command 4))