From f7e929441c32a5fac60c9e8a1b8e2f5ab87b58ac Mon Sep 17 00:00:00 2001 From: hns Date: Mon, 14 Mar 2005 16:30:32 +0000 Subject: [PATCH] Implement our own RhinoDebugger subclass to display scripts in a nice table. The current implementation is merged from helma_1_4 branch and a bit quirky in regard to repository script source names --- src/helma/scripting/rhino/RhinoCore.java | 26 +- src/helma/scripting/rhino/RhinoEngine.java | 3 +- .../scripting/rhino/debug/HelmaDebugger.java | 258 ++++++++++++++++++ 3 files changed, 276 insertions(+), 11 deletions(-) create mode 100644 src/helma/scripting/rhino/debug/HelmaDebugger.java diff --git a/src/helma/scripting/rhino/RhinoCore.java b/src/helma/scripting/rhino/RhinoCore.java index 87e510e8..7e3b7f2a 100644 --- a/src/helma/scripting/rhino/RhinoCore.java +++ b/src/helma/scripting/rhino/RhinoCore.java @@ -17,6 +17,8 @@ package helma.scripting.rhino; import helma.scripting.rhino.extensions.*; +import helma.scripting.rhino.debug.HelmaDebugger; +import helma.scripting.rhino.debug.ScopeProvider; import helma.framework.core.*; import helma.framework.repository.Resource; import helma.objectmodel.*; @@ -27,8 +29,6 @@ import helma.util.CacheMap; import helma.util.SystemMap; import helma.util.WrappedMap; import org.mozilla.javascript.*; -import org.mozilla.javascript.tools.debugger.Main; -import org.mozilla.javascript.tools.debugger.ScopeProvider; import java.io.*; import java.text.*; @@ -65,7 +65,7 @@ public final class RhinoCore implements ScopeProvider { // Any error that may have been found in global code String globalError; - Main debugger = null; + HelmaDebugger debugger = null; /** * Create a Rhino evaluator for the given application and request evaluator. @@ -86,12 +86,7 @@ public final class RhinoCore implements ScopeProvider { // Set up visual debugger if rhino.debug = true if ("true".equals(app.getProperty("rhino.debug"))) { - debugger = new Main(app.getName() + " Debugger"); - debugger.setScopeProvider(this); - debugger.pack(); - debugger.setVisible(true); - debugger.contextCreated(context); - debugger.contextEntered(context); + initDebugger(context); } // Set default optimization level according to whether debugger is on @@ -153,6 +148,19 @@ public final class RhinoCore implements ScopeProvider { } } + void initDebugger(Context context) { + if (debugger == null) { + debugger = new HelmaDebugger(app.getName()); + debugger.setScopeProvider(this); + debugger.pack(); + debugger.setLocation(60, 60); + } + if (!debugger.isVisible()) + debugger.setVisible(true); + debugger.contextCreated(context); + debugger.contextEntered(context); + } + /** * Initialize the evaluator, making sure the minimum type information * necessary to bootstrap the rest is parsed. diff --git a/src/helma/scripting/rhino/RhinoEngine.java b/src/helma/scripting/rhino/RhinoEngine.java index 9cea9e9c..34156b8f 100644 --- a/src/helma/scripting/rhino/RhinoEngine.java +++ b/src/helma/scripting/rhino/RhinoEngine.java @@ -156,8 +156,7 @@ public class RhinoEngine implements ScriptingEngine { // if visual debugger is on let it know we're entering a context if (core.debugger != null) { - core.debugger.contextCreated(context); - core.debugger.contextEntered(context); + core.initDebugger(context); } if ("true".equals(app.getProperty("rhino.trace"))) { diff --git a/src/helma/scripting/rhino/debug/HelmaDebugger.java b/src/helma/scripting/rhino/debug/HelmaDebugger.java new file mode 100644 index 00000000..2e9270e7 --- /dev/null +++ b/src/helma/scripting/rhino/debug/HelmaDebugger.java @@ -0,0 +1,258 @@ +/* +* Helma License Notice +* +* The contents of this file are subject to the Helma License +* Version 2.0 (the "License"). You may not use this file except in +* compliance with the License. A copy of the License is available at +* http://adele.helma.org/download/helma/license.txt +* +* Copyright 1998-2003 Helma Software. All Rights Reserved. +* +* $RCSfile$ +* $Author$ +* $Revision$ +* $Date$ +*/ + +package helma.scripting.rhino.debug; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.debug.DebuggableScript; +import org.mozilla.javascript.debug.DebugFrame; + +import javax.swing.*; +import javax.swing.event.TreeSelectionListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.tree.*; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.*; + +import helma.util.StringUtils; + +public class HelmaDebugger extends Main implements TreeSelectionListener { + + JTree tree; + JList list; + DebuggerTreeNode treeRoot; + DefaultTreeModel treeModel; + HashMap treeNodes = new HashMap(); + HashMap scripts = new HashMap(); + + public HelmaDebugger(String name) { + super(name); + Container contentPane = getContentPane(); + Component main = contentPane.getComponent(1); + contentPane.remove(main); + + treeRoot = new DebuggerTreeNode(name); + tree = new JTree(treeRoot); + treeModel = new DefaultTreeModel(treeRoot); + tree.setModel(treeModel); + tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + tree.addTreeSelectionListener(this); + // tree.setRootVisible(false); + // track double clicks + tree.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent evt) { + openScript(tree.getSelectionPath()); + } + }); + // track enter key + tree.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent evt) { + if (evt.getKeyCode() == KeyEvent.VK_ENTER) + openScript(tree.getSelectionPath()); + } + }); + JScrollPane treeScroller = new JScrollPane(tree); + treeScroller.setPreferredSize(new Dimension(180, 300)); + + list = new JList(); + // no bold font lists for me, thanks + list.setFont(list.getFont().deriveFont(Font.PLAIN)); + list.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent evt) { + openFunction((String) list.getSelectedValue()); + } + }); + list.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent evt) { + if (evt.getKeyCode() == KeyEvent.VK_ENTER) + openFunction((String) list.getSelectedValue()); + } + }); + JScrollPane listScroller = new JScrollPane(list); + listScroller.setPreferredSize(new Dimension(180, 200)); + + JSplitPane split1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + split1.setTopComponent(treeScroller); + split1.setBottomComponent(listScroller); + split1.setOneTouchExpandable(true); + Main.setResizeWeight(split1, 0.66); + + JSplitPane split2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + split2.setLeftComponent(split1); + split2.setRightComponent(main); + split2.setOneTouchExpandable(true); + contentPane.add(split2, BorderLayout.CENTER); + } + + public void setVisible(boolean b) { + super.setVisible(b); + // hide console window + console.hide(); + } + + public void handleCompilationDone(Context cx, DebuggableScript fnOrScript, + String source) { + String sourceName = fnOrScript.getSourceName(); + if (!treeNodes.containsKey(sourceName)) { + createTreeNode(sourceName); + } + super.handleCompilationDone(cx, fnOrScript, source); + } + + void createTreeNode(String sourceName) { + String[] path = StringUtils.split(sourceName, ":/\\"); + DebuggerTreeNode node = treeRoot; + DebuggerTreeNode newNode = null; + for (int i = 1; i < path.length; i++) { + DebuggerTreeNode n = node.get(path[i]); + if (n == null) { + n = new DebuggerTreeNode(path[i]); + node.add(n); + if (newNode == null) newNode = n; + } + node = n; + } + treeNodes.put(sourceName, node); + scripts.put(node, sourceName); + if (newNode != null) { + SwingUtilities.invokeLater(new NodeInserter(newNode)); + } + } + + void openScript(TreePath path) { + if (path == null) + return; + Object node = path.getLastPathComponent(); + if (node == null) + return; + String script = (String) scripts.get(node); + if (script == null) + return; + JInternalFrame w = (JInternalFrame) fileWindows.get(script); + if (w != null) { + try { + if (w.isIcon()) + w.setMaximum(true); + w.show(); + w.setSelected(true); + } catch (Exception exc) { + } + } else { + SourceInfo si = (SourceInfo) sourceNames.get(script); + if (si == null) { + System.out.println("debugger error: Couldn't find source: " + script); + } + swingInvoke(CreateFileWindow.action(this, si, -1)); + } + // display functions for opened script file + Vector functions = new Vector(); + Iterator it = functionNames.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = (Map.Entry) it.next(); + ScriptItem si = (ScriptItem) entry.getValue(); + if (script.equals(si.getSourceInfo().getUrl())) { + functions.add(entry.getKey()); + } + } + Collections.sort(functions); + list.setListData(functions); + } + + void openFunction(String function) { + if (function == null) + return; + ScriptItem item = (ScriptItem) functionNames.get(function); + if (item != null) { + SourceInfo si = item.getSourceInfo(); + String url = si.getUrl(); + int lineNumber = item.getFirstLine(); + FileWindow w = getFileWindow(url); + if (w == null) { + CreateFileWindow.action(this, si, lineNumber).run(); + w = getFileWindow(url); + w.setPosition(-1); + } + int start = w.getPosition(lineNumber - 1); + int end = w.getPosition(lineNumber) - 1; + w.textArea.select(start); + w.textArea.setCaretPosition(start); + w.textArea.moveCaretPosition(end); + try { + if (w.isIcon()) + w.setMaximum(true); + w.show(); + requestFocus(); + w.requestFocus(); + w.textArea.requestFocus(); + } catch (Exception exc) { + } + } + } + + public void valueChanged(TreeSelectionEvent e) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) + tree.getLastSelectedPathComponent(); + + if (node == null) return; + + Object script = scripts.get(node); + if (script != null) { + // openScript(script); + } + } + + class DebuggerTreeNode extends DefaultMutableTreeNode { + + public DebuggerTreeNode(Object obj) { + super(obj); + } + + public DebuggerTreeNode get(String name) { + Enumeration children = this.children(); + while (children.hasMoreElements()) { + DebuggerTreeNode node = (DebuggerTreeNode) children.nextElement(); + if (node != null && name.equals(node.getUserObject())) + return node; + } + return null; + } + } + + class NodeInserter implements Runnable { + MutableTreeNode node; + + NodeInserter(MutableTreeNode node) { + this.node = node; + } + + public void run() { + MutableTreeNode parent = (MutableTreeNode) node.getParent(); + if (parent == treeRoot && treeRoot.getChildCount() == 1) { + tree.makeVisible(new TreePath(new Object[]{parent, node})); + } + treeModel.insertNodeInto(node, parent, parent.getIndex(node)); + } + } + +} +