;;; jde-stack.el --- Jump to source from Java stack trace
;; Copyright (C) 1999 Phillip Lord
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Status:
;;
;; This mostly works. It should be considered to be a beta release as
;; I havent tested it out heavily yet. If you find this software
;; useful then please tell me, and one day I shall possible be able to
;; call it full release.
;;
;; The current version should be available at my website. I havent got
;; a URL for this at the moment, so you will have to resort to a web
;; search. You can search on my name if you like, and have endless fun
;; wading through all the christian references. "Guide me O thou Great
;; Redeemer". Altavista gives far too much bread to get to heaven
;; these days...
;;; Commentary:
;;
;; This package provides the ability to jump from stack traces to the
;; relevant part of the java source. There is one main entry point
;; which is a mouse-1 click on the stack trace, which will jump to the
;; source suggested at this point. This should also work in other
;; buffers (for instance log files), by calling directly the function
;; `jde-stack-show-at-point'. Following this entry point two functions
;; `jde-stack-show-next' and `jde-stack-show-prev' allow cycling
;; backwards and forwards through the stack trace. Error messages are
;; given when the end of the stack is reached.
;;; Bugs:
;;
;; Its a little bit limited at the moment, in that it parses by a
;; simple regexp. It will also fail totally if a JIT is being
;; used. It also uses the JDE to translate between the unqualified
;; class name, and the full name, even though its given in the stack
;; trace. This is entirely the fault of the JVM which gives stack
;; traces of the form (Blah.java:999). This is so easy to regexp
;; search, that I couldnt turn down the oppurtunity.
;;; History:
;;
;; $Log: $
(require 'jde)
;;; Code:
(defvar jde-stack-current-marker nil
"Stores a marker where the last stack was.")
(defun jde-stack-show-class-stack(class line current-window)
;; jde-show-class-source switches automatically to other
;; window. Makes sense there, screws things up here. So switching to
;; window -1 here makes it switch back to where we started. Arrgh!!!
(if current-window
(other-window -1))
(jde-show-class-source class)
(set-buffer (concat class ".java" ))
(goto-line (string-to-number line)))
(defun jde-stack-show-at-mouse (event)
"Jump to the stack at the mouse click."
(interactive "e" )
(save-excursion
(set-buffer (jde-stack-url-event-buffer event))
(goto-char (jde-stack-event-point event))
(jde-stack-show-at-point)))
(defun jde-stack-show-at-point()
"Displays the stack on this current line"
(interactive)
(if (not (jde-stack-show-on-line))
(message "Unable to parse stack on this line")))
(defun jde-stack-show-on-line(&optional current-window)
"Show the stack trace on this line. If non-nil CURRENT-WINDOW specifies
to show the in the current window."
(save-excursion
(let ((window
current-window)
(line-ending
(progn(end-of-line)
(point))))
(beginning-of-line)
(if (re-search-forward "\\([A-Za-z0-9]*\\)\\([.]java\\)\\(:\\)\\([0-9]*\\)" line-ending 't)
(progn
(move-marker
(jde-stack-get-create-marker)
(point))
(jde-stack-show-class-stack (match-string 1) (match-string 4) window))))))
;;stolen from browse-url. I have no idea what they do. Perhaps they
;;are useless, who can tell???
(defun jde-stack-url-event-buffer (event)
(window-buffer (posn-window (event-start event))))
(defun jde-stack-event-point (event)
(posn-point (event-start event)))
(defun jde-stack-get-create-marker()
(if jde-stack-current-marker
jde-stack-current-marker
(setq jde-stack-current-marker (make-marker))))
(defun jde-stack-point-to-marker()
(let ((marker (jde-stack-get-create-marker)))
(if (not marker)
(error "No stack at point")
(set-buffer (marker-buffer marker))
(goto-char (marker-position marker)))))
(defun jde-stack-next()
"Shows the source next in the stack trace"
(interactive)
(jde-stack-point-to-marker)
(forward-line 1)
(if(not (jde-stack-show-on-line 't))
(message "The end of the stack has been reached" )))
(defun jde-stack-prev()
"Shows the source previous in the stack trace"
(interactive)
(jde-stack-point-to-marker)
(forward-line -1)
(if(not (jde-stack-show-on-line 't))
(message "The start of the stack has been reached" )))
;;The jde defines a unique mode extended from comint for running
;;processes. Its fairly much identical to comint. Its quite handy
;;here.
(add-hook 'jde-run-mode-hook
'(lambda()
(define-key (current-local-map) "\C-c\C-v\C-[" 'jde-stack-prev)
(define-key (current-local-map) "\C-c\C-v\C-]" 'jde-stack-next)
(define-key (current-local-map) [mouse-1] 'jde-stack-show-at-mouse)))
(define-key jde-mode-map "\C-c\C-v\C-[" 'jde-stack-prev)
(define-key jde-mode-map "\C-c\C-v\C-]" 'jde-stack-next)
(provide 'jde-stack)
;;; jde-stack.el ends here