Hunt down some redundant events
This commit is contained in:
@ -8,7 +8,6 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -23,14 +22,14 @@ 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.DesignerRoot;
|
||||
import net.sourceforge.pmd.util.fxdesigner.model.XPathEvaluationException;
|
||||
import net.sourceforge.pmd.util.fxdesigner.popups.EventLogController;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.AbstractController;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.CompositeSelectionSource;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.DesignerRoot;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.NodeSelectionSource;
|
||||
import net.sourceforge.pmd.util.fxdesigner.model.XPathEvaluationException;
|
||||
import net.sourceforge.pmd.util.fxdesigner.popups.EventLogController;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.LimitedSizeStack;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.NodeSelectionSource;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.SoftReferenceCache;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.TextAwareNodeWrapper;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil;
|
||||
@ -148,12 +147,8 @@ public class MainDesignerController extends AbstractController<AbstractControlle
|
||||
refreshAST(); // initial refreshing
|
||||
sourceEditorController.moveCaret(0, 0);
|
||||
|
||||
// ignore selection events produced in very short delay
|
||||
// this avoids event handling loops, e.g. an event from
|
||||
// the xpath panel is forwarded to the treeView, which
|
||||
// forwards back an event, etc.
|
||||
getSelectionEvents().thenIgnoreFor(Duration.ofMillis(20))
|
||||
.subscribe(n -> CompositeSelectionSource.super.bubbleDown(n));
|
||||
// this is the only place where getSelectionEvents is called
|
||||
getSelectionEvents().distinct().subscribe(n -> CompositeSelectionSource.super.bubbleDown(n));
|
||||
}
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.reactfx.EventStream;
|
||||
import org.reactfx.EventStreams;
|
||||
import org.reactfx.SuspendableEventStream;
|
||||
import org.reactfx.value.Var;
|
||||
|
||||
import net.sourceforge.pmd.internal.util.IteratorUtil;
|
||||
@ -29,9 +30,9 @@ 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.model.MetricResult;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.AbstractController;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.NodeSelectionSource;
|
||||
import net.sourceforge.pmd.util.fxdesigner.model.MetricResult;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentProperty;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.controls.ScopeHierarchyTreeCell;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.controls.ScopeHierarchyTreeItem;
|
||||
@ -83,6 +84,9 @@ public class NodeInfoPanelController extends AbstractController<MainDesignerCont
|
||||
|
||||
private Node selectedNode;
|
||||
|
||||
private SuspendableEventStream<TreeItem<Object>> myScopeItemSelectionEvents;
|
||||
|
||||
|
||||
public NodeInfoPanelController(MainDesignerController mainController) {
|
||||
super(mainController);
|
||||
}
|
||||
@ -101,17 +105,19 @@ public class NodeInfoPanelController extends AbstractController<MainDesignerCont
|
||||
.distinct()
|
||||
.subscribe(show -> displayAttributes(selectedNode));
|
||||
|
||||
// suppress as early as possible in the pipeline
|
||||
myScopeItemSelectionEvents = EventStreams.valuesOf(scopeHierarchyTreeView.getSelectionModel().selectedItemProperty()).suppressible();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public EventStream<NodeSelectionEvent> getSelectionEvents() {
|
||||
return EventStreams.valuesOf(scopeHierarchyTreeView.getSelectionModel().selectedItemProperty())
|
||||
.filter(Objects::nonNull)
|
||||
.map(TreeItem::getValue)
|
||||
.filterMap(o -> o instanceof NameDeclaration, o -> (NameDeclaration) o)
|
||||
.map(NameDeclaration::getNode)
|
||||
.map(n -> new NodeSelectionEvent(n, this));
|
||||
return myScopeItemSelectionEvents.filter(Objects::nonNull)
|
||||
.map(TreeItem::getValue)
|
||||
.filterMap(o -> o instanceof NameDeclaration, o -> (NameDeclaration) o)
|
||||
.map(NameDeclaration::getNode)
|
||||
.map(n -> new NodeSelectionEvent(n, this));
|
||||
|
||||
}
|
||||
|
||||
@ -213,7 +219,8 @@ public class NodeInfoPanelController extends AbstractController<MainDesignerCont
|
||||
// you selected is in its own scope hierarchy so it looks buggy.
|
||||
int maxDepth = IteratorUtil.count(parentIterator(previousSelection, true));
|
||||
rootScope.tryFindNode(previousSelection.getValue(), maxDepth)
|
||||
.ifPresent(scopeHierarchyTreeView.getSelectionModel()::select);
|
||||
// suspend notifications while selecting
|
||||
.ifPresent(item -> myScopeItemSelectionEvents.suspendWhile(() -> scopeHierarchyTreeView.getSelectionModel().select(item)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import org.controlsfx.validation.ValidationSupport;
|
||||
import org.controlsfx.validation.Validator;
|
||||
import org.reactfx.EventStream;
|
||||
import org.reactfx.EventStreams;
|
||||
import org.reactfx.SuspendableEventStream;
|
||||
import org.reactfx.collection.LiveArrayList;
|
||||
import org.reactfx.value.Val;
|
||||
import org.reactfx.value.Var;
|
||||
@ -27,14 +28,14 @@ import net.sourceforge.pmd.lang.LanguageVersion;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.rule.XPathRule;
|
||||
import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.AbstractController;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.LogEntry.Category;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.NodeSelectionSource;
|
||||
import net.sourceforge.pmd.util.fxdesigner.model.ObservableXPathRuleBuilder;
|
||||
import net.sourceforge.pmd.util.fxdesigner.model.XPathEvaluationException;
|
||||
import net.sourceforge.pmd.util.fxdesigner.model.XPathEvaluator;
|
||||
import net.sourceforge.pmd.util.fxdesigner.popups.ExportXPathWizardController;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.AbstractController;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.NodeSelectionSource;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.TextAwareNodeWrapper;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.autocomplete.CompletionResultSource;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.autocomplete.XPathAutocompleteProvider;
|
||||
@ -100,6 +101,7 @@ public class XPathPanelController extends AbstractController<MainDesignerControl
|
||||
// ui property
|
||||
private Var<String> xpathVersionUIProperty = Var.newSimpleVar(XPathRuleQuery.XPATH_2_0);
|
||||
|
||||
private SuspendableEventStream<TextAwareNodeWrapper> selectionEvents;
|
||||
|
||||
public XPathPanelController(MainDesignerController mainController) {
|
||||
super(mainController);
|
||||
@ -127,7 +129,7 @@ public class XPathPanelController extends AbstractController<MainDesignerControl
|
||||
.or(xpathVersionProperty().changes())
|
||||
.subscribe(tick -> parent.refreshXPathResults());
|
||||
|
||||
|
||||
selectionEvents = EventStreams.valuesOf(xpathResultListView.getSelectionModel().selectedItemProperty()).suppressible();
|
||||
}
|
||||
|
||||
|
||||
@ -219,11 +221,9 @@ public class XPathPanelController extends AbstractController<MainDesignerControl
|
||||
|
||||
@Override
|
||||
public EventStream<NodeSelectionEvent> getSelectionEvents() {
|
||||
return EventStreams.valuesOf(xpathResultListView.getSelectionModel().selectedItemProperty())
|
||||
.conditionOn(xpathResultListView.focusedProperty())
|
||||
.filter(Objects::nonNull)
|
||||
.map(TextAwareNodeWrapper::getNode)
|
||||
.map(n -> new NodeSelectionEvent(n, this));
|
||||
return selectionEvents.filter(Objects::nonNull)
|
||||
.map(TextAwareNodeWrapper::getNode)
|
||||
.map(n -> new NodeSelectionEvent(n, this));
|
||||
}
|
||||
|
||||
|
||||
@ -232,7 +232,7 @@ public class XPathPanelController extends AbstractController<MainDesignerControl
|
||||
xpathResultListView.getItems().stream()
|
||||
.filter(wrapper -> wrapper.getNode().equals(node))
|
||||
.findFirst()
|
||||
.ifPresent(xpathResultListView.getSelectionModel()::select);
|
||||
.ifPresent(item -> selectionEvents.suspendWhile(() -> xpathResultListView.getSelectionModel().select(item)));
|
||||
}
|
||||
|
||||
|
||||
|
@ -30,6 +30,11 @@ public interface CompositeSelectionSource extends NodeSelectionSource {
|
||||
}
|
||||
|
||||
|
||||
default boolean isRoot() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
default void setFocusNode(Node node) {
|
||||
// by default do nothing,
|
||||
|
@ -23,13 +23,10 @@ import org.reactfx.collection.LiveArrayList;
|
||||
import org.reactfx.collection.LiveList;
|
||||
import org.reactfx.value.Val;
|
||||
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.DesignerRoot;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.LogEntry;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.LogEntry.Category;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.LogEntry.LogEntryWithData;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.ApplicationComponent;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
|
||||
import net.sourceforge.pmd.util.fxdesigner.app.NodeSelectionSource.NodeSelectionEvent;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
|
||||
|
||||
|
||||
/**
|
||||
@ -55,6 +52,7 @@ public class EventLogger implements ApplicationComponent {
|
||||
this.designerRoot = designerRoot; // we have to be careful with initialization order here
|
||||
|
||||
EventStream<LogEntryWithData<NodeSelectionEvent>> eventTraces =
|
||||
// none of this is done if developer mode isn't enabled because then those events aren't even pushed in the first place
|
||||
reduceEntangledIfPossible(filterOnCategory(latestEvent, false, SELECTION_EVENT_TRACING).map(t -> (LogEntryWithData<NodeSelectionEvent>) t),
|
||||
// the user data for those is the event
|
||||
// if they're the same event we reduce them together
|
||||
|
@ -33,7 +33,12 @@ public interface NodeSelectionSource extends ApplicationComponent {
|
||||
/**
|
||||
* Returns a stream of events that should push an event each time
|
||||
* this source or one of its sub components records a change in node
|
||||
* selection.
|
||||
* selection. This one needs to be implemented in sub classes.
|
||||
*
|
||||
* <p>You can't trust that this method will return the same stream
|
||||
* when called several times. In fact it's just called one time.
|
||||
* That's why you can't abstract the suppressible behaviour here.
|
||||
* You'd need Scala traits.
|
||||
*/
|
||||
EventStream<NodeSelectionEvent> getSelectionEvents();
|
||||
|
||||
|
@ -12,7 +12,6 @@ import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.reactfx.EventSource;
|
||||
import org.reactfx.EventStream;
|
||||
import org.reactfx.EventStreams;
|
||||
import org.reactfx.SuspendableEventStream;
|
||||
import org.reactfx.value.Var;
|
||||
@ -37,25 +36,25 @@ public class AstTreeView extends TreeView<Node> implements NodeSelectionSource {
|
||||
private final TreeViewWrapper<Node> myWrapper = new TreeViewWrapper<>(this);
|
||||
|
||||
private ASTTreeItem selectedTreeItem;
|
||||
private final SuspendableEventStream<Node> pausableEvents;
|
||||
private final SuspendableEventStream<Node> selectionEvents;
|
||||
private DesignerRoot designerRoot;
|
||||
|
||||
|
||||
public AstTreeView() {
|
||||
EventSource<Node> selectionEvents = new EventSource<>();
|
||||
pausableEvents = selectionEvents.pausable();
|
||||
EventSource<Node> eventSink = new EventSource<>();
|
||||
selectionEvents = eventSink.suppressible();
|
||||
|
||||
// push a node selection event whenever...
|
||||
// * The selection changes
|
||||
EventStreams.valuesOf(getSelectionModel().selectedItemProperty())
|
||||
.filterMap(Objects::nonNull, TreeItem::getValue)
|
||||
.subscribe(selectionEvents::push);
|
||||
.subscribe(eventSink::push);
|
||||
|
||||
// * a cell is explicitly clicked. This catches the case where the cell was already selected
|
||||
// * the currently selected cell is explicitly clicked
|
||||
setCellFactory(tv -> new ASTTreeCell(n -> {
|
||||
// only push an event if the node was already selected
|
||||
if (selectedTreeItem != null && selectedTreeItem.getValue() != null && selectedTreeItem.getValue().equals(n)) {
|
||||
selectionEvents.push(n);
|
||||
eventSink.push(n);
|
||||
}
|
||||
}));
|
||||
|
||||
@ -63,8 +62,8 @@ public class AstTreeView extends TreeView<Node> implements NodeSelectionSource {
|
||||
|
||||
|
||||
@Override
|
||||
public EventStream<NodeSelectionEvent> getSelectionEvents() {
|
||||
return pausableEvents.map(n -> new NodeSelectionEvent(n, this));
|
||||
public SuspendableEventStream<NodeSelectionEvent> getSelectionEvents() {
|
||||
return selectionEvents.map(n -> new NodeSelectionEvent(n, this)).suppressible();
|
||||
}
|
||||
|
||||
|
||||
@ -82,7 +81,8 @@ public class AstTreeView extends TreeView<Node> implements NodeSelectionSource {
|
||||
|
||||
ASTTreeItem found = ((ASTTreeItem) getRoot()).findItem(node);
|
||||
if (found != null) {
|
||||
pausableEvents.suspendWhile(() -> selectionModel.select(found));
|
||||
// don't fire any selection event while itself setting the selected item
|
||||
selectionEvents.suspendWhile(() -> selectionModel.select(found));
|
||||
}
|
||||
|
||||
highlightFocusNodeParents(selectedTreeItem, found);
|
||||
|
Reference in New Issue
Block a user