diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/MainDesignerController.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/MainDesignerController.java index 33e86645ed..a0dffe1b8d 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/MainDesignerController.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/MainDesignerController.java @@ -17,11 +17,11 @@ import java.util.Stack; import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; +import org.reactfx.Subscription; import org.reactfx.value.Val; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; import net.sourceforge.pmd.util.fxdesigner.app.AbstractController; import net.sourceforge.pmd.util.fxdesigner.app.CompositeSelectionSource; import net.sourceforge.pmd.util.fxdesigner.app.DesignerRoot; @@ -133,7 +133,11 @@ public class MainDesignerController extends AbstractController sourceEditorController.showAuxclasspathSetupPopup()); - openEventLogMenuItem.setOnAction(e -> eventLogController.getValue().showPopup()); + openEventLogMenuItem.setOnAction(e -> { + EventLogController wizard = eventLogController.getValue(); + Subscription parentToWizSubscription = wizard.errorNodesProperty().values().subscribe(sourceEditorController.currentErrorNodesProperty()::setValue); + wizard.showPopup(parentToWizSubscription); + }); openEventLogMenuItem.textProperty().bind( getLogger().numNewLogEntriesProperty().map(i -> "Event log (" + (i > 0 ? i : "no") + " new)") ); @@ -145,10 +149,12 @@ public class MainDesignerController extends AbstractController CompositeSelectionSource.super.bubbleDown(n)); + } @@ -199,15 +205,6 @@ public class MainDesignerController extends AbstractController occurrences) { - sourceEditorController.highlightNameOccurrences(occurrences); - } - /** * Runs an XPath (2.0) query on the current AST. * Performs no side effects. @@ -223,31 +220,6 @@ public class MainDesignerController extends AbstractController n) { - resetSelectedErrorNodes(); - sourceEditorController.highlightErrorNodes(n); - } - - public void resetSelectedErrorNodes() { - sourceEditorController.clearErrorNodes(); - } - - public void resetXPathResults() { - sourceEditorController.clearXPathHighlight(); - } - - /** Replaces previously highlighted XPath results with the given nodes. */ - public void highlightXPathResults(List nodes) { - sourceEditorController.highlightXPathResults(nodes); - } - private void showLicensePopup() { Alert licenseAlert = new Alert(AlertType.INFORMATION); licenseAlert.setWidth(500); diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/NodeInfoPanelController.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/NodeInfoPanelController.java index 6b9cfea346..5fc874fbc4 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/NodeInfoPanelController.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/NodeInfoPanelController.java @@ -8,13 +8,9 @@ import static net.sourceforge.pmd.util.fxdesigner.util.DesignerIteratorUtil.pare import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Objects; -import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; import org.reactfx.EventStream; @@ -27,9 +23,6 @@ import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.xpath.Attribute; import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; import net.sourceforge.pmd.lang.symboltable.NameDeclaration; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; -import net.sourceforge.pmd.lang.symboltable.Scope; -import net.sourceforge.pmd.lang.symboltable.ScopedNode; import net.sourceforge.pmd.util.fxdesigner.app.AbstractController; import net.sourceforge.pmd.util.fxdesigner.app.NodeSelectionSource; import net.sourceforge.pmd.util.fxdesigner.model.MetricResult; @@ -122,13 +115,6 @@ public class NodeInfoPanelController extends AbstractController displayAttributes(node)); Platform.runLater(() -> displayMetrics(node)); displayScopes(node); - - if (node instanceof ScopedNode) { - // not null as well - highlightNameOccurences((ScopedNode) node); - } - } - - - private void highlightNameOccurences(ScopedNode node) { - - // For MethodNameDeclaration the scope is the method scope, which is not the scope it is declared - // in but the scope it declares! That means that getDeclarations().get(declaration) returns null - // and no name occurrences are found. We thus look in the parent, but ultimately the name occurrence - // finder is broken since it can't find e.g. the use of a method in another scope. Plus in case of - // overloads both overloads are reported to have a usage. - - // Plus this is some serious law of Demeter breaking there... - - Set candidates = new HashSet<>(node.getScope().getDeclarations().keySet()); - - Optional.ofNullable(node.getScope().getParent()) - .map(Scope::getDeclarations) - .map(Map::keySet) - .ifPresent(candidates::addAll); - - List occurrences = - candidates.stream() - .filter(nd -> node.equals(nd.getNode())) - .findFirst() - .map(nd -> { - // nd.getScope() != nd.getNode().getScope()?? wtf? - - List usages = nd.getNode().getScope().getDeclarations().get(nd); - - if (usages == null) { - usages = nd.getNode().getScope().getParent().getDeclarations().get(nd); - } - - return usages; - }) - .orElse(Collections.emptyList()); - - parent.highlightAsNameOccurences(occurrences); } diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/SourceEditorController.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/SourceEditorController.java index 9c11c4e47f..8874cb19df 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/SourceEditorController.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/SourceEditorController.java @@ -12,10 +12,14 @@ import java.io.IOException; import java.time.Duration; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.function.IntFunction; import java.util.stream.Collectors; @@ -27,7 +31,10 @@ import org.reactfx.value.Var; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.symboltable.NameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; +import net.sourceforge.pmd.lang.symboltable.Scope; +import net.sourceforge.pmd.lang.symboltable.ScopedNode; import net.sourceforge.pmd.util.ClasspathClassLoader; import net.sourceforge.pmd.util.fxdesigner.app.AbstractController; import net.sourceforge.pmd.util.fxdesigner.app.CompositeSelectionSource; @@ -79,6 +86,8 @@ public class SourceEditorController extends AbstractController currentFocusNode = Var.newSimpleVar(null); + private final Var> currentRuleResults = Var.newSimpleVar(Collections.emptyList()); + private final Var> currentErrorNodes = Var.newSimpleVar(Collections.emptyList()); private final Var> auxclasspathFiles = Var.newSimpleVar(emptyList()); private final Val auxclasspathClassLoader = auxclasspathFiles.map(fileList -> { @@ -132,12 +141,15 @@ public class SourceEditorController extends AbstractController codeEditorArea.styleNodes(node == null ? emptyList() : singleton(node), StyleLayerIds.FOCUS, true)); + if (node instanceof ScopedNode) { + // not null as well + Platform.runLater(() -> highlightNameOccurrences(getNameOccurrences((ScopedNode) node))); + + } + } + + + private List getNameOccurrences(ScopedNode node) { + + // For MethodNameDeclaration the scope is the method scope, which is not the scope it is declared + // in but the scope it declares! That means that getDeclarations().get(declaration) returns null + // and no name occurrences are found. We thus look in the parent, but ultimately the name occurrence + // finder is broken since it can't find e.g. the use of a method in another scope. Plus in case of + // overloads both overloads are reported to have a usage. + + // Plus this is some serious law of Demeter breaking there... + + Set candidates = new HashSet<>(node.getScope().getDeclarations().keySet()); + + Optional.ofNullable(node.getScope().getParent()) + .map(Scope::getDeclarations) + .map(Map::keySet) + .ifPresent(candidates::addAll); + + return candidates.stream() + .filter(nd -> node.equals(nd.getNode())) + .findFirst() + .map(nd -> { + // nd.getScope() != nd.getNode().getScope()?? wtf? + + List usages = nd.getNode().getScope().getDeclarations().get(nd); + + if (usages == null) { + usages = nd.getNode().getScope().getParent().getDeclarations().get(nd); + } + + return usages; + }) + .orElse(Collections.emptyList()); } /** Highlights xpath results (xpath highlight). */ - public void highlightXPathResults(Collection nodes) { + private void highlightXPathResults(Collection nodes) { codeEditorArea.styleNodes(nodes, StyleLayerIds.XPATH_RESULT, true); } /** Highlights name occurrences (secondary highlight). */ - public void highlightNameOccurrences(Collection occs) { + private void highlightNameOccurrences(Collection occs) { codeEditorArea.styleNodes(occs.stream().map(NameOccurrence::getLocation).collect(Collectors.toList()), StyleLayerIds.NAME_OCCURENCE, true); } /** Highlights nodes that are in error (secondary highlight). */ - public void highlightErrorNodes(Collection nodes) { + private void highlightErrorNodes(Collection nodes) { codeEditorArea.styleNodes(nodes, StyleLayerIds.ERROR, true); if (!nodes.isEmpty()) { scrollEditorToNode(nodes.iterator().next()); @@ -298,6 +332,16 @@ public class SourceEditorController extends AbstractController> currentRuleResultsProperty() { + return currentRuleResults; + } + + + public Var> currentErrorNodesProperty() { + return currentErrorNodes; + } + + /** Scroll the editor to a node and makes it visible. */ private void scrollEditorToNode(Node node) { @@ -318,13 +362,8 @@ public class SourceEditorController extends AbstractController xpathResultListView; + private final Var> currentResults = Var.newSimpleVar(Collections.emptyList()); + // ui property private Var xpathVersionUIProperty = Var.newSimpleVar(XPathRuleQuery.XPATH_2_0); @@ -261,7 +263,7 @@ public class XPathPanelController extends AbstractController> currentResultsProperty() { + return currentResults; + } public String getXpathExpression() { return xpathExpressionArea.getText(); diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/popups/EventLogController.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/popups/EventLogController.java index 3caea486a8..bfe0fc2135 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/popups/EventLogController.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/popups/EventLogController.java @@ -4,16 +4,16 @@ package net.sourceforge.pmd.util.fxdesigner.popups; +import static org.reactfx.EventStreams.valuesOf; + import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.time.Duration; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.kordamp.ikonli.javafx.FontIcon; -import org.reactfx.EventStreams; import org.reactfx.Subscription; import org.reactfx.value.Val; import org.reactfx.value.Var; @@ -175,25 +175,12 @@ public final class EventLogController extends AbstractController { - if (b) { - parent.handleSelectedNodeInError(selectedErrorNodes.getValue()); - } else { - parent.resetSelectedErrorNodes(); - } - }) - ); - - binding = binding.and( - selectedErrorNodes.values().subscribe(parent::handleSelectedNodeInError) - ); + // reset error nodes on closing + binding = binding.and(() -> selectedErrorNodes.setValue(Collections.emptyList())); SortedList logEntries = new SortedList<>(getLogger().getLog(), Comparator.reverseOrder()); eventLogTableView.itemsProperty().setValue(logEntries); @@ -211,8 +198,9 @@ public final class EventLogController extends AbstractController hidePopup()); } - public void hidePopup() { + private void hidePopup() { myPopupStage.hide(); popupBinding.unsubscribe(); popupBinding = () -> {}; @@ -244,6 +233,10 @@ public final class EventLogController extends AbstractController> errorNodesProperty() { + return selectedErrorNodes; + } + private Val titleProperty() { return parent.getLogger().numNewLogEntriesProperty().map(i -> "Event log (" + (i > 0 ? i : "no") + " new)"); }