mirror of
https://github.com/processing/processing4.git
synced 2026-02-04 06:09:17 +01:00
ASTGen: break the revised stuff out into PDEX
This commit is contained in:
@@ -29,10 +29,10 @@ import processing.app.ui.Toolkit;
|
||||
import processing.mode.java.debug.LineBreakpoint;
|
||||
import processing.mode.java.debug.LineHighlight;
|
||||
import processing.mode.java.debug.LineID;
|
||||
import processing.mode.java.pdex.ASTGenerator;
|
||||
import processing.mode.java.pdex.ErrorCheckerService;
|
||||
import processing.mode.java.pdex.ImportStatement;
|
||||
import processing.mode.java.pdex.JavaTextArea;
|
||||
import processing.mode.java.pdex.PDEX;
|
||||
import processing.mode.java.pdex.Problem;
|
||||
import processing.mode.java.pdex.SourceUtils;
|
||||
import processing.mode.java.preproc.PdePreprocessor;
|
||||
@@ -71,6 +71,7 @@ public class JavaEditor extends Editor {
|
||||
private boolean javaTabWarned;
|
||||
|
||||
protected ErrorCheckerService errorCheckerService;
|
||||
protected PDEX pdex;
|
||||
|
||||
protected List<Problem> problems = Collections.emptyList();
|
||||
|
||||
@@ -183,6 +184,21 @@ public class JavaEditor extends Editor {
|
||||
}
|
||||
errorCheckerService.start();
|
||||
errorCheckerService.notifySketchChanged();
|
||||
|
||||
pdex = new PDEX(this, errorCheckerService);
|
||||
|
||||
// Add ctrl+click listener
|
||||
getJavaTextArea().getPainter().addMouseListener(new MouseAdapter() {
|
||||
public void mouseReleased(MouseEvent evt) {
|
||||
if (evt.getButton() == MouseEvent.BUTTON1) {
|
||||
if ((evt.isControlDown() && !Platform.isMacOS()) || evt.isMetaDown()) {
|
||||
handleCtrlClick(evt);
|
||||
}
|
||||
} else if (evt.getButton() == MouseEvent.BUTTON2) {
|
||||
handleCtrlClick(evt);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -212,6 +228,7 @@ public class JavaEditor extends Editor {
|
||||
if (errorCheckerService != null) {
|
||||
if (hasJavaTabsChanged) {
|
||||
errorCheckerService.handleHasJavaTabsChange(hasJavaTabs);
|
||||
pdex.handleHasJavaTabsChange(hasJavaTabs);
|
||||
if (hasJavaTabs) {
|
||||
setProblemList(Collections.emptyList());
|
||||
}
|
||||
@@ -1358,6 +1375,7 @@ public class JavaEditor extends Editor {
|
||||
inspector.dispose();
|
||||
}
|
||||
errorCheckerService.stop();
|
||||
pdex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -2795,25 +2813,31 @@ public class JavaEditor extends Editor {
|
||||
|
||||
/** Handle refactor operation */
|
||||
private void handleRefactor() {
|
||||
Messages.log("Caret at:" + textarea.getLineText(textarea.getCaretLine()));
|
||||
ASTGenerator astGenerator = errorCheckerService.getASTGenerator();
|
||||
int startOffset = getSelectionStart();
|
||||
int stopOffset = getSelectionStop();
|
||||
int tabIndex = sketch.getCurrentCodeIndex();
|
||||
|
||||
astGenerator.handleRename(tabIndex, startOffset, stopOffset);
|
||||
pdex.handleRename(tabIndex, startOffset, stopOffset);
|
||||
}
|
||||
|
||||
|
||||
/** Handle show usage operation */
|
||||
private void handleShowUsage() {
|
||||
Messages.log("Caret at:" + textarea.getLineText(textarea.getCaretLine()));
|
||||
ASTGenerator astGenerator = errorCheckerService.getASTGenerator();
|
||||
int startOffset = getSelectionStart();
|
||||
int stopOffset = getSelectionStop();
|
||||
int tabIndex = sketch.getCurrentCodeIndex();
|
||||
|
||||
astGenerator.handleShowUsage(tabIndex, startOffset, stopOffset);
|
||||
pdex.handleShowUsage(tabIndex, startOffset, stopOffset);
|
||||
}
|
||||
|
||||
|
||||
/** Handle ctrl+click */
|
||||
private void handleCtrlClick(MouseEvent evt) {
|
||||
int off = getJavaTextArea().xyToOffset(evt.getX(), evt.getY());
|
||||
if (off < 0) return;
|
||||
int tabIndex = sketch.getCurrentCodeIndex();
|
||||
|
||||
pdex.handleCtrlClick(tabIndex, off);
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
183
java/src/processing/mode/java/pdex/ASTUtils.java
Normal file
183
java/src/processing/mode/java/pdex/ASTUtils.java
Normal file
@@ -0,0 +1,183 @@
|
||||
package processing.mode.java.pdex;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
|
||||
import org.eclipse.jdt.core.dom.IBinding;
|
||||
import org.eclipse.jdt.core.dom.IMethodBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.Name;
|
||||
import org.eclipse.jdt.core.dom.NodeFinder;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.Type;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import processing.app.Messages;
|
||||
|
||||
|
||||
public class ASTUtils {
|
||||
|
||||
public static ASTNode getASTNodeAt(ASTNode root, int startJavaOffset, int stopJavaOffset) {
|
||||
Messages.log("* getASTNodeAt");
|
||||
|
||||
int length = stopJavaOffset - startJavaOffset;
|
||||
|
||||
NodeFinder f = new NodeFinder(root, startJavaOffset, length);
|
||||
ASTNode node = f.getCoveredNode();
|
||||
if (node == null) {
|
||||
node = f.getCoveringNode();
|
||||
}
|
||||
if (node == null) {
|
||||
Messages.log("no node found");
|
||||
} else {
|
||||
Messages.log("found " + node.getClass().getSimpleName());
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
public static SimpleName getSimpleNameAt(ASTNode root, int startJavaOffset, int stopJavaOffset) {
|
||||
Messages.log("* getSimpleNameAt");
|
||||
|
||||
// Find node at offset
|
||||
ASTNode node = getASTNodeAt(root, startJavaOffset, stopJavaOffset);
|
||||
|
||||
SimpleName result = null;
|
||||
|
||||
if (node == null) {
|
||||
result = null;
|
||||
} else if (node.getNodeType() == ASTNode.SIMPLE_NAME) {
|
||||
result = (SimpleName) node;
|
||||
} else {
|
||||
// Return SimpleName with highest coverage
|
||||
List<SimpleName> simpleNames = getSimpleNameChildren(node);
|
||||
if (!simpleNames.isEmpty()) {
|
||||
// Compute coverage <selection x node>
|
||||
int[] coverages = simpleNames.stream()
|
||||
.mapToInt(name -> {
|
||||
int start = name.getStartPosition();
|
||||
int stop = start + name.getLength();
|
||||
return Math.min(stop, stopJavaOffset) -
|
||||
Math.max(startJavaOffset, start);
|
||||
})
|
||||
.toArray();
|
||||
// Select node with highest coverage
|
||||
int maxIndex = IntStream.range(0, simpleNames.size())
|
||||
.filter(i -> coverages[i] >= 0)
|
||||
.reduce((i, j) -> coverages[i] > coverages[j] ? i : j)
|
||||
.orElse(-1);
|
||||
if (maxIndex == -1) return null;
|
||||
result = simpleNames.get(maxIndex);
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
Messages.log("no simple name found");
|
||||
} else {
|
||||
Messages.log("found " + node.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static List<SimpleName> getSimpleNameChildren(ASTNode node) {
|
||||
List<SimpleName> simpleNames = new ArrayList<>();
|
||||
node.accept(new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(SimpleName simpleName) {
|
||||
simpleNames.add(simpleName);
|
||||
return super.visit(simpleName);
|
||||
}
|
||||
});
|
||||
return simpleNames;
|
||||
}
|
||||
|
||||
|
||||
public static IBinding resolveBinding(SimpleName node) {
|
||||
IBinding binding = node.resolveBinding();
|
||||
if (binding == null) return null;
|
||||
|
||||
// Fix constructor call/declaration being resolved as type
|
||||
if (binding.getKind() == IBinding.TYPE) {
|
||||
ASTNode context = node;
|
||||
|
||||
// Go up until we find non Name or Type node
|
||||
// stop if context is type argument (parent is also Name/Type, but unrelated)
|
||||
while (isNameOrType(context) &&
|
||||
!context.getLocationInParent().getId().equals("typeArguments")) {
|
||||
context = context.getParent();
|
||||
}
|
||||
|
||||
switch (context.getNodeType()) {
|
||||
case ASTNode.METHOD_DECLARATION:
|
||||
MethodDeclaration decl = (MethodDeclaration) context;
|
||||
if (decl.isConstructor()) {
|
||||
binding = decl.resolveBinding();
|
||||
}
|
||||
break;
|
||||
case ASTNode.CLASS_INSTANCE_CREATION:
|
||||
ClassInstanceCreation cic = (ClassInstanceCreation) context;
|
||||
binding = cic.resolveConstructorBinding();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (binding == null) return null;
|
||||
|
||||
// Normalize parametrized and raw bindings into generic bindings
|
||||
switch (binding.getKind()) {
|
||||
case IBinding.TYPE:
|
||||
ITypeBinding type = (ITypeBinding) binding;
|
||||
if (type.isParameterizedType() || type.isRawType()) {
|
||||
binding = type.getErasure();
|
||||
}
|
||||
break;
|
||||
case IBinding.METHOD:
|
||||
IMethodBinding method = (IMethodBinding) binding;
|
||||
ITypeBinding declaringClass = method.getDeclaringClass();
|
||||
if (declaringClass.isParameterizedType() ||
|
||||
declaringClass.isRawType()) {
|
||||
IMethodBinding[] methods = declaringClass.getErasure().getDeclaredMethods();
|
||||
IMethodBinding generic = Arrays.stream(methods)
|
||||
.filter(method::overrides)
|
||||
.findAny().orElse(null);
|
||||
if (generic != null) method = generic;
|
||||
}
|
||||
if (method.isParameterizedMethod() || method.isRawMethod()) {
|
||||
method = method.getMethodDeclaration();
|
||||
}
|
||||
binding = method;
|
||||
break;
|
||||
}
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isNameOrType(ASTNode node) {
|
||||
return node instanceof Name || node instanceof Type;
|
||||
}
|
||||
|
||||
|
||||
protected static List<SimpleName> findAllOccurrences(ASTNode root, String bindingKey) {
|
||||
List<SimpleName> occurences = new ArrayList<>();
|
||||
root.getRoot().accept(new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(SimpleName name) {
|
||||
IBinding binding = resolveBinding(name);
|
||||
if (binding != null && bindingKey.equals(binding.getKey())) {
|
||||
occurences.add(name);
|
||||
}
|
||||
return super.visit(name);
|
||||
}
|
||||
});
|
||||
|
||||
return occurences;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -99,11 +99,6 @@ public class ErrorCheckerService {
|
||||
*/
|
||||
private volatile boolean running;
|
||||
|
||||
/**
|
||||
* ASTGenerator for operations on AST
|
||||
*/
|
||||
protected final ASTGenerator astGenerator;
|
||||
|
||||
/**
|
||||
* Error checking doesn't happen before this interval has ellapsed since the
|
||||
* last request() call.
|
||||
@@ -137,7 +132,6 @@ public class ErrorCheckerService {
|
||||
|
||||
public ErrorCheckerService(JavaEditor editor) {
|
||||
this.editor = editor;
|
||||
astGenerator = new ASTGenerator(editor, this);
|
||||
isEnabled = !editor.hasJavaTabs();
|
||||
isContinuousCheckEnabled = JavaMode.errorCheckEnabled;
|
||||
registerDoneListener(errorHandlerListener);
|
||||
@@ -170,8 +164,6 @@ public class ErrorCheckerService {
|
||||
Messages.loge("problem in error checker loop", e);
|
||||
}
|
||||
}
|
||||
|
||||
astGenerator.getGui().disposeAllWindows();
|
||||
}
|
||||
|
||||
|
||||
@@ -303,11 +295,6 @@ public class ErrorCheckerService {
|
||||
}
|
||||
|
||||
|
||||
public ASTGenerator getASTGenerator() {
|
||||
return astGenerator;
|
||||
}
|
||||
|
||||
|
||||
protected final DocumentListener sketchChangedListener = new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
@@ -929,9 +916,6 @@ public class ErrorCheckerService {
|
||||
notifySketchChanged();
|
||||
} else {
|
||||
preprocessingTask.cancel(false);
|
||||
if (astGenerator.getGui().showUsageBinding != null) {
|
||||
astGenerator.getGui().showUsageWindow.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,8 @@ public class JavaTextArea extends JEditTextArea {
|
||||
prevMMotionListeners = painter.getMouseMotionListeners();
|
||||
prevKeyListeners = editor.getKeyListeners();
|
||||
|
||||
suggestionGenerator = new ASTGenerator();
|
||||
|
||||
tweakMode = false;
|
||||
}
|
||||
|
||||
@@ -254,6 +256,8 @@ public class JavaTextArea extends JEditTextArea {
|
||||
}
|
||||
|
||||
|
||||
ASTGenerator suggestionGenerator;
|
||||
|
||||
SwingWorker<Void, Void> suggestionWorker = null;
|
||||
|
||||
volatile boolean suggestionRunning = false;
|
||||
@@ -331,8 +335,7 @@ public class JavaTextArea extends JEditTextArea {
|
||||
if (phrase != null) {
|
||||
List<CompletionCandidate> candidates;
|
||||
|
||||
ASTGenerator astGenerator = editor.getErrorChecker().getASTGenerator();
|
||||
candidates = astGenerator.preparePredictions(ps, phrase, lineNumber);
|
||||
candidates = suggestionGenerator.preparePredictions(ps, phrase, lineNumber);
|
||||
|
||||
if (!suggestionRequested) {
|
||||
|
||||
|
||||
@@ -47,7 +47,6 @@ import javax.swing.text.Utilities;
|
||||
|
||||
import processing.app.Messages;
|
||||
import processing.app.Mode;
|
||||
import processing.app.Platform;
|
||||
import processing.app.SketchCode;
|
||||
import processing.app.syntax.SyntaxDocument;
|
||||
import processing.app.syntax.TextAreaDefaults;
|
||||
@@ -77,20 +76,6 @@ public class JavaTextAreaPainter extends TextAreaPainter
|
||||
public JavaTextAreaPainter(final JavaTextArea textArea, TextAreaDefaults defaults) {
|
||||
super(textArea, defaults);
|
||||
|
||||
addMouseListener(new MouseAdapter() {
|
||||
public void mouseReleased(MouseEvent evt) {
|
||||
if (!getJavaEditor().hasJavaTabs()) { // Ctrl + Click disabled for java tabs
|
||||
if (evt.getButton() == MouseEvent.BUTTON1) {
|
||||
if ((evt.isControlDown() && !Platform.isMacOS()) || evt.isMetaDown()) {
|
||||
handleCtrlClick(evt);
|
||||
}
|
||||
} else if (evt.getButton() == MouseEvent.BUTTON2) {
|
||||
handleCtrlClick(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle mouse clicks to toggle breakpoints
|
||||
addMouseListener(new MouseAdapter() {
|
||||
long lastTime; // OS X seems to be firing multiple mouse events
|
||||
@@ -120,19 +105,6 @@ public class JavaTextAreaPainter extends TextAreaPainter
|
||||
cursorType = Cursor.DEFAULT_CURSOR;
|
||||
}
|
||||
|
||||
|
||||
void handleCtrlClick(MouseEvent evt) {
|
||||
int off = textArea.xyToOffset(evt.getX(), evt.getY());
|
||||
if (off < 0) return;
|
||||
|
||||
int tabIndex = getEditor().getSketch().getCurrentCodeIndex();
|
||||
|
||||
ASTGenerator astGenerator = getJavaEditor().getErrorChecker().getASTGenerator();
|
||||
|
||||
astGenerator.handleCtrlClick(tabIndex, off);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Paint a line. Paints the gutter (with background color and text) then the
|
||||
* line (background color and text).
|
||||
|
||||
843
java/src/processing/mode/java/pdex/PDEX.java
Normal file
843
java/src/processing/mode/java/pdex/PDEX.java
Normal file
@@ -0,0 +1,843 @@
|
||||
package processing.mode.java.pdex;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.IBinding;
|
||||
import org.eclipse.jdt.core.dom.IMethodBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.IVariableBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.jdt.core.dom.VariableDeclaration;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.TreeModel;
|
||||
|
||||
import processing.app.Messages;
|
||||
import processing.app.Sketch;
|
||||
import processing.app.ui.EditorStatus;
|
||||
import processing.app.ui.Toolkit;
|
||||
import processing.mode.java.JavaEditor;
|
||||
import processing.mode.java.pdex.PreprocessedSketch.SketchInterval;
|
||||
|
||||
import static processing.mode.java.pdex.ASTUtils.*;
|
||||
|
||||
public class PDEX {
|
||||
|
||||
private static final boolean SHOW_DEBUG_TREE = true;
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
private ShowUsage showUsage;
|
||||
private Rename rename;
|
||||
private DebugTree debugTree;
|
||||
|
||||
private JavaEditor editor;
|
||||
private ErrorCheckerService ecs;
|
||||
|
||||
|
||||
public PDEX(JavaEditor editor, ErrorCheckerService ecs) {
|
||||
this.editor = editor;
|
||||
this.ecs = ecs;
|
||||
|
||||
this.enabled = !editor.hasJavaTabs();
|
||||
|
||||
showUsage = new ShowUsage(editor, ecs);
|
||||
rename = new Rename(editor);
|
||||
if (SHOW_DEBUG_TREE) {
|
||||
debugTree = new DebugTree(editor, ecs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void handleShowUsage(int tabIndex, int startTabOffset, int stopTabOffset) {
|
||||
Messages.log("* handleShowUsage");
|
||||
if (!enabled) return; // show usage disabled if java tabs
|
||||
ecs.acceptWhenDone(ps -> showUsage.findUsageAndUpdateTree(ps, tabIndex, startTabOffset, stopTabOffset));
|
||||
}
|
||||
|
||||
|
||||
public void handleRename(int tabIndex, int startTabOffset, int stopTabOffset) {
|
||||
Messages.log("* handleRename");
|
||||
if (!enabled) return; // refactoring disabled w/ java tabs
|
||||
ecs.acceptWhenDone(ps -> rename.handleRename(ps, tabIndex, startTabOffset, stopTabOffset));
|
||||
}
|
||||
|
||||
|
||||
public void handleCtrlClick(int tabIndex, int offset) {
|
||||
Messages.log("* handleCtrlClick");
|
||||
if (!enabled) return; // disabled w/ java tabs
|
||||
ecs.acceptWhenDone(ps -> handleCtrlClick(ps, tabIndex, offset));
|
||||
}
|
||||
|
||||
|
||||
public void handleHasJavaTabsChange(boolean hasJavaTabs) {
|
||||
enabled = !hasJavaTabs;
|
||||
if (!enabled) {
|
||||
showUsage.hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void dispose() {
|
||||
showUsage.dispose();
|
||||
rename.dispose();
|
||||
if (debugTree != null) {
|
||||
debugTree.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Thread: worker
|
||||
private void handleCtrlClick(PreprocessedSketch ps, int tabIndex, int offset) {
|
||||
ASTNode root = ps.compilationUnit;
|
||||
int javaOffset = ps.tabOffsetToJavaOffset(tabIndex, offset);
|
||||
|
||||
SimpleName simpleName = getSimpleNameAt(root, javaOffset, javaOffset);
|
||||
|
||||
if (simpleName == null) {
|
||||
Messages.log("no simple name found at click location");
|
||||
return;
|
||||
}
|
||||
|
||||
IBinding binding = resolveBinding(simpleName);
|
||||
if (binding == null) {
|
||||
Messages.log("binding not resolved");
|
||||
return;
|
||||
}
|
||||
|
||||
String key = binding.getKey();
|
||||
ASTNode decl = ps.compilationUnit.findDeclaringNode(key);
|
||||
if (decl == null) {
|
||||
Messages.log("decl not found, showing usage instead");
|
||||
showUsage.findUsageAndUpdateTree(ps, binding);
|
||||
return;
|
||||
}
|
||||
|
||||
SimpleName declName = null;
|
||||
switch (binding.getKind()) {
|
||||
case IBinding.TYPE: declName = ((TypeDeclaration) decl).getName(); break;
|
||||
case IBinding.METHOD: declName = ((MethodDeclaration) decl).getName(); break;
|
||||
case IBinding.VARIABLE: declName = ((VariableDeclaration) decl).getName(); break;
|
||||
}
|
||||
if (declName == null) {
|
||||
Messages.log("decl name not found " + decl);
|
||||
return;
|
||||
}
|
||||
|
||||
if (declName.equals(simpleName)) {
|
||||
showUsage.findUsageAndUpdateTree(ps, binding);
|
||||
} else {
|
||||
Messages.log("found declaration, offset " + decl.getStartPosition() + ", name: " + declName);
|
||||
SketchInterval si = ps.mapJavaToSketch(declName);
|
||||
EventQueue.invokeLater(() -> {
|
||||
editor.highlight(si.tabIndex, si.startTabOffset, si.stopTabOffset);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class ShowUsage {
|
||||
|
||||
final JDialog window;
|
||||
final JTree tree;
|
||||
|
||||
final JavaEditor editor;
|
||||
final ErrorCheckerService ecs;
|
||||
|
||||
final Consumer<PreprocessedSketch> reloadListener;
|
||||
|
||||
IBinding binding;
|
||||
|
||||
|
||||
ShowUsage(JavaEditor editor, ErrorCheckerService ecs) {
|
||||
this.editor = editor;
|
||||
this.ecs = ecs;
|
||||
|
||||
reloadListener = this::reloadShowUsage;
|
||||
|
||||
{ // Show Usage window
|
||||
window = new JDialog(editor);
|
||||
window.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
|
||||
window.setAutoRequestFocus(false);
|
||||
window.addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentHidden(ComponentEvent e) {
|
||||
// Delete references to ASTNodes so that whole AST can be GC'd
|
||||
binding = null;
|
||||
tree.setModel(null);
|
||||
ecs.unregisterDoneListener(reloadListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentShown(ComponentEvent e) {
|
||||
ecs.registerDoneListener(reloadListener);
|
||||
}
|
||||
});
|
||||
window.setSize(300, 400);
|
||||
Toolkit.setIcon(window);
|
||||
JScrollPane sp2 = new JScrollPane();
|
||||
tree = new JTree();
|
||||
DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) tree.getCellRenderer();
|
||||
renderer.setLeafIcon(null);
|
||||
renderer.setClosedIcon(null);
|
||||
renderer.setOpenIcon(null);
|
||||
sp2.setViewportView(tree);
|
||||
window.add(sp2);
|
||||
}
|
||||
|
||||
tree.addTreeSelectionListener(e -> {
|
||||
if (tree.getLastSelectedPathComponent() == null) {
|
||||
return;
|
||||
}
|
||||
DefaultMutableTreeNode tnode = (DefaultMutableTreeNode) tree
|
||||
.getLastSelectedPathComponent();
|
||||
|
||||
if (tnode.getUserObject() instanceof ShowUsageTreeNode) {
|
||||
ShowUsageTreeNode node = (ShowUsageTreeNode) tnode.getUserObject();
|
||||
editor.highlight(node.tabIndex, node.startTabOffset, node.stopTabOffset);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Thread: worker
|
||||
void findUsageAndUpdateTree(PreprocessedSketch ps, int tabIndex,
|
||||
int startTabOffset, int stopTabOffset) {
|
||||
// Map offsets
|
||||
int startJavaOffset = ps.tabOffsetToJavaOffset(tabIndex, startTabOffset);
|
||||
int stopJavaOffset = ps.tabOffsetToJavaOffset(tabIndex, stopTabOffset);
|
||||
|
||||
// Find the node
|
||||
SimpleName name = ASTUtils.getSimpleNameAt(ps.compilationUnit, startJavaOffset, stopJavaOffset);
|
||||
if (name == null) return;
|
||||
|
||||
// Find binding
|
||||
IBinding binding = ASTUtils.resolveBinding(name);
|
||||
if (binding == null) return;
|
||||
|
||||
this.binding = binding;
|
||||
|
||||
findUsageAndUpdateTree(ps, binding);
|
||||
}
|
||||
|
||||
|
||||
// Thread: worker
|
||||
void findUsageAndUpdateTree(PreprocessedSketch ps, IBinding binding) {
|
||||
|
||||
// Get label
|
||||
String bindingType = "";
|
||||
switch (binding.getKind()) {
|
||||
case IBinding.METHOD:
|
||||
IMethodBinding method = (IMethodBinding) binding;
|
||||
if (method.isConstructor()) bindingType = "Constructor";
|
||||
else bindingType = "Method";
|
||||
break;
|
||||
case IBinding.TYPE:
|
||||
bindingType = "Type";
|
||||
break;
|
||||
case IBinding.VARIABLE:
|
||||
IVariableBinding variable = (IVariableBinding) binding;
|
||||
if (variable.isField()) bindingType = "Field";
|
||||
else if (variable.isParameter()) bindingType = "Parameter";
|
||||
else if (variable.isEnumConstant()) bindingType = "Enum constant";
|
||||
else bindingType = "Local variable";
|
||||
break;
|
||||
}
|
||||
|
||||
String elementName = binding.getName();
|
||||
|
||||
// Create root node
|
||||
DefaultMutableTreeNode rootNode =
|
||||
new DefaultMutableTreeNode(bindingType + ": " + elementName);
|
||||
|
||||
int usageCount;
|
||||
|
||||
{ // Find usages, map to tree nodes, add to root node
|
||||
String bindingKey = binding.getKey();
|
||||
List<SketchInterval> intervals =
|
||||
findAllOccurrences(ps.compilationUnit, bindingKey).stream()
|
||||
.map(ps::mapJavaToSketch)
|
||||
// TODO: this has to be fixed with better token mapping
|
||||
// remove occurrences which fall into generated header
|
||||
.filter(in -> in.tabIndex != 0 ||
|
||||
(in.startTabOffset >= 0 && in.stopTabOffset > 0))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
usageCount = intervals.size();
|
||||
|
||||
Map<Integer, List<ShowUsageTreeNode>> tabGroupedTreeNodes = intervals.stream()
|
||||
// Convert to TreeNodes
|
||||
.map(in -> ShowUsageTreeNode.fromSketchInterval(ps, in))
|
||||
// Group by tab
|
||||
.collect(Collectors.groupingBy(node -> node.tabIndex));
|
||||
|
||||
tabGroupedTreeNodes.entrySet().stream()
|
||||
// Sort by tab index
|
||||
.sorted(Comparator.comparing(Map.Entry::getKey))
|
||||
.map(entry -> {
|
||||
Integer tabIndex = entry.getKey();
|
||||
List<ShowUsageTreeNode> nodes = entry.getValue();
|
||||
|
||||
int count = nodes.size();
|
||||
String usageLabel = count == 1 ? "usage" : "usages";
|
||||
|
||||
// Create new DefaultMutableTreeNode for this tab
|
||||
String tabLabel = "<html><font color=#222222>" +
|
||||
ps.sketch.getCode(tabIndex).getPrettyName() +
|
||||
"</font> <font color=#999999>" + count + " " + usageLabel + "</font></html>";
|
||||
DefaultMutableTreeNode tabNode = new DefaultMutableTreeNode(tabLabel);
|
||||
|
||||
nodes.stream()
|
||||
// Convert TreeNodes to DefaultMutableTreeNodes
|
||||
.map(DefaultMutableTreeNode::new)
|
||||
// Add all as children of tab node
|
||||
.forEach(tabNode::add);
|
||||
return tabNode;
|
||||
})
|
||||
// Add all tab nodes as children of root node
|
||||
.forEach(rootNode::add);
|
||||
}
|
||||
|
||||
TreeModel treeModel = new DefaultTreeModel(rootNode);
|
||||
|
||||
// Update tree
|
||||
EventQueue.invokeLater(() -> {
|
||||
tree.setModel(treeModel);
|
||||
|
||||
// Expand all nodes
|
||||
for (int i = 0; i < tree.getRowCount(); i++) {
|
||||
tree.expandRow(i);
|
||||
}
|
||||
|
||||
tree.setRootVisible(true);
|
||||
|
||||
if (!window.isVisible()) {
|
||||
window.setVisible(true);
|
||||
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
GraphicsDevice defaultScreen = ge.getDefaultScreenDevice();
|
||||
Rectangle rect = defaultScreen.getDefaultConfiguration().getBounds();
|
||||
int maxX = (int) rect.getMaxX() - window.getWidth();
|
||||
int x = Math.min(editor.getX() + editor.getWidth(), maxX);
|
||||
int y = (x == maxX) ? 10 : editor.getY();
|
||||
window.setLocation(x, y);
|
||||
}
|
||||
window.toFront();
|
||||
window.setTitle("Usage of \"" + elementName + "\" : " +
|
||||
usageCount + " time(s)");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Thread: worker
|
||||
void reloadShowUsage(PreprocessedSketch ps) {
|
||||
if (binding != null) {
|
||||
findUsageAndUpdateTree(ps, binding);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void hide() {
|
||||
window.setVisible(false);
|
||||
}
|
||||
|
||||
|
||||
void dispose() {
|
||||
if (window != null) {
|
||||
window.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class ShowUsageTreeNode {
|
||||
|
||||
final int tabIndex;
|
||||
final int startTabOffset;
|
||||
final int stopTabOffset;
|
||||
|
||||
final String text;
|
||||
|
||||
|
||||
ShowUsageTreeNode(int tabIndex, int startTabOffset, int stopTabOffset, String text) {
|
||||
this.tabIndex = tabIndex;
|
||||
this.startTabOffset = startTabOffset;
|
||||
this.stopTabOffset = stopTabOffset;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
|
||||
static ShowUsageTreeNode fromSketchInterval(PreprocessedSketch ps, SketchInterval in) {
|
||||
int lineStartPdeOffset = ps.pdeCode.lastIndexOf('\n', in.startPdeOffset) + 1;
|
||||
int lineStopPdeOffset = ps.pdeCode.indexOf('\n', in.stopPdeOffset);
|
||||
if (lineStopPdeOffset == -1) lineStopPdeOffset = ps.pdeCode.length();
|
||||
|
||||
int highlightStartOffset = in.startPdeOffset - lineStartPdeOffset;
|
||||
int highlightStopOffset = in.stopPdeOffset - lineStartPdeOffset;
|
||||
|
||||
int tabLine = ps.tabOffsetToTabLine(in.tabIndex, in.startTabOffset);
|
||||
|
||||
// TODO: what a mess
|
||||
String line = ps.pdeCode.substring(lineStartPdeOffset, lineStopPdeOffset);
|
||||
String pre = line.substring(0, highlightStartOffset)
|
||||
.replace("&", "&").replace(">", ">").replace("<", "<");
|
||||
String highlight = line.substring(highlightStartOffset, highlightStopOffset)
|
||||
.replace("&", "&").replace(">", ">").replace("<", "<");
|
||||
String post = line.substring(highlightStopOffset)
|
||||
.replace("&", "&").replace(">", ">").replace("<", "<");
|
||||
line = pre + "<font color=#222222><b>" + highlight + "</b></font>" + post;
|
||||
line = line.trim();
|
||||
|
||||
|
||||
String text = "<html><font color=#bbbbbb>" +
|
||||
(tabLine + 1) + "</font> <font color=#777777>" + line + "</font></html>";
|
||||
|
||||
return new ShowUsageTreeNode(in.tabIndex, in.startTabOffset, in.stopTabOffset, text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class Rename {
|
||||
|
||||
final JDialog window;
|
||||
final JTextField textField;
|
||||
final JLabel oldNameLabel;
|
||||
|
||||
final JavaEditor editor;
|
||||
|
||||
IBinding binding;
|
||||
PreprocessedSketch ps;
|
||||
|
||||
|
||||
Rename(JavaEditor editor) {
|
||||
this.editor = editor;
|
||||
|
||||
window = new JDialog(editor);
|
||||
window.setTitle("Enter new name:");
|
||||
window.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
|
||||
window.setModal(true);
|
||||
window.setResizable(false);
|
||||
window.addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentHidden(ComponentEvent e) {
|
||||
binding = null;
|
||||
ps = null;
|
||||
}
|
||||
});
|
||||
window.setSize(250, 130);
|
||||
window.setLayout(new BoxLayout(window.getContentPane(), BoxLayout.Y_AXIS));
|
||||
Toolkit.setIcon(window);
|
||||
|
||||
{ // Top panel
|
||||
|
||||
// Text field
|
||||
textField = new JTextField();
|
||||
textField.setPreferredSize(new Dimension(150, 60));
|
||||
|
||||
// Old name label
|
||||
oldNameLabel = new JLabel();
|
||||
oldNameLabel.setText("Old Name: ");
|
||||
|
||||
// Top panel
|
||||
JPanel panelTop = new JPanel();
|
||||
panelTop.setLayout(new BoxLayout(panelTop, BoxLayout.Y_AXIS));
|
||||
panelTop.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
panelTop.add(textField);
|
||||
panelTop.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
panelTop.add(oldNameLabel);
|
||||
window.add(panelTop);
|
||||
}
|
||||
|
||||
{ // Bottom panel
|
||||
JButton showUsageButton = new JButton("Show Usage");
|
||||
showUsageButton.addActionListener(e -> {
|
||||
showUsage.findUsageAndUpdateTree(ps, binding);
|
||||
window.setVisible(false);
|
||||
});
|
||||
|
||||
JButton renameButton = new JButton("Rename");
|
||||
renameButton.addActionListener(e -> {
|
||||
if (textField.getText().length() == 0) {
|
||||
return;
|
||||
}
|
||||
String newName = textField.getText().trim();
|
||||
boolean isNewNameValid = newName.length() >= 1 &&
|
||||
newName.chars().limit(1).allMatch(Character::isUnicodeIdentifierStart) &&
|
||||
newName.chars().skip(1).allMatch(Character::isUnicodeIdentifierPart);
|
||||
if (!isNewNameValid) {
|
||||
JOptionPane.showMessageDialog(new JFrame(), "'" + newName
|
||||
+ "' isn't a valid name.", "Uh oh..", JOptionPane.PLAIN_MESSAGE);
|
||||
} else {
|
||||
rename(ps, binding, newName);
|
||||
window.setVisible(false);
|
||||
}
|
||||
});
|
||||
|
||||
JPanel panelBottom = new JPanel();
|
||||
panelBottom.setLayout(new BoxLayout(panelBottom, BoxLayout.X_AXIS));
|
||||
panelBottom.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
panelBottom.add(Box.createHorizontalGlue());
|
||||
panelBottom.add(showUsageButton);
|
||||
panelBottom.add(Box.createRigidArea(new Dimension(15, 0)));
|
||||
panelBottom.add(renameButton);
|
||||
window.add(panelBottom);
|
||||
}
|
||||
|
||||
window.setMinimumSize(window.getSize());
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Thread: worker
|
||||
void handleRename(PreprocessedSketch ps, int tabIndex, int startTabOffset, int stopTabOffset) {
|
||||
if (ps.hasSyntaxErrors) {
|
||||
editor.statusMessage("Can't perform action until syntax errors are fixed :(",
|
||||
EditorStatus.WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
ASTNode root = ps.compilationUnit;
|
||||
|
||||
// Map offsets
|
||||
int startJavaOffset = ps.tabOffsetToJavaOffset(tabIndex, startTabOffset);
|
||||
int stopJavaOffset = ps.tabOffsetToJavaOffset(tabIndex, stopTabOffset);
|
||||
|
||||
// Find the node
|
||||
SimpleName name = getSimpleNameAt(root, startJavaOffset, stopJavaOffset);
|
||||
if (name == null) {
|
||||
editor.statusMessage("Highlight the class/function/variable name first",
|
||||
EditorStatus.NOTICE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find binding
|
||||
IBinding binding = resolveBinding(name);
|
||||
if (binding == null) {
|
||||
editor.statusMessage(name.getIdentifier() + " isn't defined in this sketch, " +
|
||||
"so it cannot be renamed", EditorStatus.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ASTNode decl = ps.compilationUnit.findDeclaringNode(binding.getKey());
|
||||
if (decl == null) {
|
||||
editor.statusMessage(name.getIdentifier() + " isn't defined in this sketch, " +
|
||||
"so it cannot be renamed", EditorStatus.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Display the rename dialog
|
||||
EventQueue.invokeLater(() -> {
|
||||
if (!window.isVisible()) {
|
||||
this.ps = ps;
|
||||
this.binding = binding;
|
||||
window.setLocation(editor.getX()
|
||||
+ (editor.getWidth() - window.getWidth()) / 2,
|
||||
editor.getY()
|
||||
+ (editor.getHeight() - window.getHeight())
|
||||
/ 2);
|
||||
oldNameLabel.setText("Current name: " + binding.getName());
|
||||
textField.setText(binding.getName());
|
||||
textField.requestFocus();
|
||||
textField.selectAll();
|
||||
window.setVisible(true);
|
||||
window.toFront();
|
||||
|
||||
int x = editor.getX() + (editor.getWidth() - window.getWidth()) / 2;
|
||||
int y = editor.getY() + (editor.getHeight() - window.getHeight()) / 2;
|
||||
window.setLocation(x, y);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Thread: EDT (we can't allow user to mess with sketch while renaming)
|
||||
void rename(PreprocessedSketch ps, IBinding binding, String newName) {
|
||||
CompilationUnit root = ps.compilationUnit;
|
||||
|
||||
// Renaming constructor should rename class
|
||||
if (binding.getKind() == IBinding.METHOD) {
|
||||
IMethodBinding method = (IMethodBinding) binding;
|
||||
if (method.isConstructor()) {
|
||||
binding = method.getDeclaringClass();
|
||||
}
|
||||
}
|
||||
|
||||
ASTNode decl = root.findDeclaringNode(binding.getKey());
|
||||
if (decl == null) return;
|
||||
|
||||
showUsage.hide();
|
||||
|
||||
List<SimpleName> occurrences = new ArrayList<>();
|
||||
occurrences.addAll(findAllOccurrences(root, binding.getKey()));
|
||||
|
||||
// Renaming class should rename all constructors
|
||||
if (binding.getKind() == IBinding.TYPE) {
|
||||
ITypeBinding type = (ITypeBinding) binding;
|
||||
//type = type.getErasure();
|
||||
IMethodBinding[] methods = type.getDeclaredMethods();
|
||||
Arrays.stream(methods)
|
||||
.filter(IMethodBinding::isConstructor)
|
||||
.flatMap(c -> findAllOccurrences(root, c.getKey()).stream())
|
||||
.forEach(occurrences::add);
|
||||
}
|
||||
|
||||
Map<Integer, List<SketchInterval>> mappedNodes = occurrences.stream()
|
||||
.map(ps::mapJavaToSketch)
|
||||
.collect(Collectors.groupingBy(interval -> interval.tabIndex));
|
||||
|
||||
Sketch sketch = ps.sketch;
|
||||
|
||||
editor.startCompoundEdit();
|
||||
|
||||
int currentTabIndex = sketch.getCurrentCodeIndex();
|
||||
final int currentOffset = editor.getCaretOffset();
|
||||
mappedNodes.entrySet().forEach(entry -> {
|
||||
int tabIndex = entry.getKey();
|
||||
sketch.setCurrentCode(tabIndex);
|
||||
|
||||
List<SketchInterval> nodes = entry.getValue();
|
||||
nodes.stream()
|
||||
// Replace from the end so all unprocess offsets stay valid
|
||||
.sorted(Comparator.comparing((SketchInterval si) -> si.startTabOffset).reversed())
|
||||
.forEach(si -> {
|
||||
// Make sure offsets are in bounds
|
||||
int length = editor.getTextArea().getDocumentLength();
|
||||
if (si.startTabOffset >= 0 && si.startTabOffset <= length &&
|
||||
si.stopTabOffset >= 0 && si.stopTabOffset <= length) {
|
||||
// Replace the code
|
||||
editor.getTextArea().select(si.startTabOffset, si.stopTabOffset);
|
||||
editor.getTextArea().setSelectedText(newName);
|
||||
}
|
||||
});
|
||||
|
||||
sketch.setModified(true);
|
||||
});
|
||||
|
||||
int precedingIntervals =
|
||||
(int) mappedNodes.getOrDefault(currentTabIndex, Collections.emptyList())
|
||||
.stream()
|
||||
.filter(interval -> interval.stopTabOffset < currentOffset)
|
||||
.count();
|
||||
int intervalLengthDiff = newName.length() - binding.getName().length();
|
||||
int offsetDiff = precedingIntervals * intervalLengthDiff;
|
||||
|
||||
sketch.setCurrentCode(currentTabIndex);
|
||||
editor.getTextArea().setCaretPosition(currentOffset + offsetDiff);
|
||||
|
||||
editor.stopCompoundEdit();
|
||||
}
|
||||
|
||||
|
||||
void dispose() {
|
||||
if (window != null) {
|
||||
window.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static class DebugTree {
|
||||
|
||||
final JDialog window;
|
||||
final JTree tree;
|
||||
|
||||
final Consumer<PreprocessedSketch> updateListener;
|
||||
|
||||
|
||||
DebugTree(JavaEditor editor, ErrorCheckerService ecs) {
|
||||
updateListener = this::buildAndUpdateTree;
|
||||
|
||||
window = new JDialog(editor);
|
||||
|
||||
tree = new JTree() {
|
||||
@Override
|
||||
public String convertValueToText(Object value, boolean selected,
|
||||
boolean expanded, boolean leaf,
|
||||
int row, boolean hasFocus) {
|
||||
if (value instanceof DefaultMutableTreeNode) {
|
||||
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
|
||||
Object o = treeNode.getUserObject();
|
||||
if (o instanceof ASTNode) {
|
||||
ASTNode node = (ASTNode) o;
|
||||
return ASTGenerator.getNodeAsString(node);
|
||||
}
|
||||
}
|
||||
return super.convertValueToText(value, selected, expanded, leaf, row, hasFocus);
|
||||
}
|
||||
};
|
||||
window.addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentHidden(ComponentEvent e) {
|
||||
ecs.unregisterDoneListener(updateListener);
|
||||
tree.setModel(null);
|
||||
}
|
||||
});
|
||||
window.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
|
||||
window.setBounds(new Rectangle(680, 100, 460, 620));
|
||||
window.setTitle("AST View - " + editor.getSketch().getName());
|
||||
JScrollPane sp = new JScrollPane();
|
||||
sp.setViewportView(tree);
|
||||
window.add(sp);
|
||||
ecs.acceptWhenDone(updateListener);
|
||||
ecs.registerDoneListener(updateListener);
|
||||
|
||||
|
||||
tree.addTreeSelectionListener(e -> {
|
||||
if (tree.getLastSelectedPathComponent() == null) {
|
||||
return;
|
||||
}
|
||||
DefaultMutableTreeNode tnode =
|
||||
(DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
|
||||
if (tnode.getUserObject() instanceof ASTNode) {
|
||||
ASTNode node = (ASTNode) tnode.getUserObject();
|
||||
ecs.acceptWhenDone(ps -> {
|
||||
SketchInterval si = ps.mapJavaToSketch(node);
|
||||
EventQueue.invokeLater(() -> {
|
||||
editor.highlight(si.tabIndex, si.startTabOffset, si.stopTabOffset);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void dispose() {
|
||||
if (window != null) {
|
||||
window.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Thread: worker
|
||||
void buildAndUpdateTree(PreprocessedSketch ps) {
|
||||
CompilationUnit cu = ps.compilationUnit;
|
||||
if (cu.types().isEmpty()){
|
||||
Messages.loge("No Type found in CU");
|
||||
return;
|
||||
}
|
||||
|
||||
ASTNode type0 = (ASTNode) cu.types().get(0);
|
||||
DefaultMutableTreeNode codeTree = new DefaultMutableTreeNode(type0);
|
||||
visitRecur(type0, codeTree);
|
||||
EventQueue.invokeLater(() -> {
|
||||
if (tree.hasFocus() || window.hasFocus()) {
|
||||
return;
|
||||
}
|
||||
tree.setModel(new DefaultTreeModel(codeTree));
|
||||
((DefaultTreeModel) tree.getModel()).reload();
|
||||
tree.validate();
|
||||
if (!window.isVisible()) {
|
||||
window.setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Thread: worker
|
||||
/**
|
||||
* Generates AST Swing component
|
||||
* @param node
|
||||
* @param tnode
|
||||
*/
|
||||
void visitRecur(ASTNode node, DefaultMutableTreeNode tnode) {
|
||||
|
||||
// TODO: nuke this, use ASTVisitor
|
||||
|
||||
Iterator<StructuralPropertyDescriptor> it =
|
||||
node.structuralPropertiesForType().iterator();
|
||||
//Base.loge("Props of " + node.getClass().getName());
|
||||
DefaultMutableTreeNode ctnode;
|
||||
while (it.hasNext()) {
|
||||
StructuralPropertyDescriptor prop = it.next();
|
||||
|
||||
if (prop.isChildProperty() || prop.isSimpleProperty()) {
|
||||
if (node.getStructuralProperty(prop) != null) {
|
||||
// System.out
|
||||
// .println(node.getStructuralProperty(prop) + " -> " + (prop));
|
||||
if (node.getStructuralProperty(prop) instanceof ASTNode) {
|
||||
ASTNode cnode = (ASTNode) node.getStructuralProperty(prop);
|
||||
if (isAddableASTNode(cnode)) {
|
||||
ctnode = new DefaultMutableTreeNode(node.getStructuralProperty(prop));
|
||||
tnode.add(ctnode);
|
||||
visitRecur(cnode, ctnode);
|
||||
}
|
||||
} else {
|
||||
tnode.add(new DefaultMutableTreeNode(node
|
||||
.getStructuralProperty(prop)));
|
||||
}
|
||||
}
|
||||
} else if (prop.isChildListProperty()) {
|
||||
List<ASTNode> nodelist = (List<ASTNode>) node.getStructuralProperty(prop);
|
||||
for (ASTNode cnode : nodelist) {
|
||||
if (isAddableASTNode(cnode)) {
|
||||
ctnode = new DefaultMutableTreeNode(cnode);
|
||||
tnode.add(ctnode);
|
||||
visitRecur(cnode, ctnode);
|
||||
} else {
|
||||
visitRecur(cnode, tnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean isAddableASTNode(ASTNode node) {
|
||||
switch (node.getNodeType()) {
|
||||
// case ASTNode.STRING_LITERAL:
|
||||
// case ASTNode.NUMBER_LITERAL:
|
||||
// case ASTNode.BOOLEAN_LITERAL:
|
||||
// case ASTNode.NULL_LITERAL:
|
||||
// return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user