Skip to content

Commit a9adefb

Browse files
committed
Bundle styling updates
That was actually the problem
1 parent 9eec595 commit a9adefb

File tree

5 files changed

+85
-44
lines changed

5 files changed

+85
-44
lines changed

pmd-ui.iml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
</list>
99
</option>
1010
</component>
11+
<component name="CheckStyle-IDEA-Module">
12+
<option name="configuration">
13+
<map />
14+
</option>
15+
</component>
1116
<component name="FacetManager">
1217
<facet type="kotlin-language" name="Kotlin">
1318
<configuration version="3" platform="JVM 1.8" allPlatforms="JVM [1.8]" useProjectSettings="false">

src/main/java/net/sourceforge/pmd/util/fxdesigner/app/NodeSelectionSource.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
import net.sourceforge.pmd.util.fxdesigner.util.controls.AstTreeView;
2020
import net.sourceforge.pmd.util.fxdesigner.util.reactfx.ReactfxUtil;
2121

22-
import javafx.application.Platform;
23-
2422

2523
/**
2624
* A control or controller that somehow displays nodes in a form that the user can select.
@@ -64,7 +62,6 @@ public interface NodeSelectionSource extends ApplicationComponent {
6462
* @param mySelectionEvents Stream of nodes that should push an event each time the user selects a node
6563
* from this control. The whole app will sync to this new selection.
6664
* @param alwaysHandleSelection Whether the component should handle selection events that originated from itself.
67-
*
6865
* @return A Val reflecting the current global selection for the app.
6966
* Note that that Val is lazy and so if you don't subscribe to it or
7067
* {@linkplain Val#pin() pin it} you won't see updates!
@@ -77,7 +74,7 @@ default Val<Node> initNodeSelectionHandling(DesignerRoot root,
7774
EventStream<NodeSelectionEvent> selection = channel.messageStream(alwaysHandleSelection, this);
7875
selection.subscribe(evt -> {
7976
try {
80-
Platform.runLater(() -> setFocusNode(evt.selected, evt.options));
77+
setFocusNode(evt.selected, evt.options);
8178
} catch (Exception e) {
8279
logInternalException(e);
8380
printShortStackTrace(e);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3+
*/
4+
5+
package net.sourceforge.pmd.util.fxdesigner.util;
6+
7+
@FunctionalInterface
8+
public interface RichRunnable extends Runnable {
9+
10+
default RichRunnable andThen(Runnable r) {
11+
return () -> {
12+
this.run();
13+
r.run();
14+
};
15+
}
16+
17+
}

src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/HighlightLayerCodeArea.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
import java.util.Set;
1616
import java.util.stream.Collectors;
1717

18+
import org.apache.commons.lang3.StringUtils;
1819
import org.checkerframework.checker.nullness.qual.NonNull;
1920
import org.checkerframework.checker.nullness.qual.Nullable;
2021
import org.fxmisc.richtext.model.StyleSpans;
2122

2223
import net.sourceforge.pmd.lang.ast.Node;
24+
import net.sourceforge.pmd.util.fxdesigner.util.RichRunnable;
2325
import net.sourceforge.pmd.util.fxdesigner.util.codearea.HighlightLayerCodeArea.LayerId;
2426

2527
import javafx.application.Platform;
@@ -74,18 +76,22 @@ public HighlightLayerCodeArea(@NamedArg("idEnum") Class<K> idEnum) {
7476
// to highlight errors that are not bound to a node, eg parsing errors
7577
// We'll need to abstract away NodeStyleSpan
7678
public void styleNodes(Collection<? extends Node> nodes, K layerId, boolean resetLayer) {
79+
updateStyling(styleNodesUpdate(nodes, layerId, resetLayer));
80+
}
81+
82+
83+
public RichRunnable styleNodesUpdate(Collection<? extends Node> nodes, K layerId, boolean resetLayer) {
7784
Objects.requireNonNull(nodes, "Pass an empty collection to represent absence, not null!");
7885

7986
if (nodes.isEmpty() && resetLayer) {
80-
clearStyleLayer(layerId);
81-
return;
87+
return layersById.get(layerId)::clearStyles;
8288
}
8389

8490
List<NodeStyleSpan> wrappedNodes = nodes.stream().map(n -> NodeStyleSpan.fromNode(n, this)).collect(Collectors.toList());
8591

8692
UniformStyleCollection collection = new UniformStyleCollection(Collections.singleton(layerId.getStyleClass()), wrappedNodes);
8793

88-
updateStyling(() -> layersById.get(layerId).styleNodes(resetLayer, collection));
94+
return () -> layersById.get(layerId).styleNodes(resetLayer, collection);
8995
}
9096

9197

@@ -97,7 +103,7 @@ public void styleNodes(Collection<? extends Node> nodes, K layerId, boolean rese
97103
*
98104
* @param update Update to carry out
99105
*/
100-
private void updateStyling(Runnable update) {
106+
public void updateStyling(Runnable update) {
101107
Platform.runLater(() -> {
102108
update.run();
103109
try {
@@ -107,7 +113,7 @@ private void updateStyling(Runnable update) {
107113
// commonly thrown when the text is being edited while
108114
// the layering algorithm runs, and it doesn't matter
109115
if ("StyleSpan's length cannot be negative".equals(e.getMessage())
110-
|| e.getMessage().contains("is not a valid range within")) {
116+
|| StringUtils.contains(e.getMessage(), "is not a valid range within")) {
111117
return;
112118
}
113119
throw new RuntimeException("Unhandled error while recomputing the styling", e);

src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/NodeEditionCodeArea.java

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.function.IntFunction;
2424

2525
import org.checkerframework.checker.nullness.qual.NonNull;
26+
import org.checkerframework.dataflow.qual.Pure;
2627
import org.fxmisc.richtext.LineNumberFactory;
2728
import org.fxmisc.richtext.event.MouseOverTextEvent;
2829
import org.reactfx.EventSource;
@@ -46,6 +47,7 @@
4647
import net.sourceforge.pmd.util.fxdesigner.model.testing.LiveViolationRecord;
4748
import net.sourceforge.pmd.util.fxdesigner.util.DataHolder;
4849
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
50+
import net.sourceforge.pmd.util.fxdesigner.util.RichRunnable;
4951
import net.sourceforge.pmd.util.fxdesigner.util.codearea.AvailableSyntaxHighlighters;
5052
import net.sourceforge.pmd.util.fxdesigner.util.codearea.HighlightLayerCodeArea;
5153
import net.sourceforge.pmd.util.fxdesigner.util.codearea.PmdCoordinatesSystem.TextPos2D;
@@ -113,9 +115,9 @@ public NodeEditionCodeArea(@NamedArg("designerRoot") DesignerRoot root) {
113115

114116
setParagraphGraphicFactory(defaultLineNumberFactory());
115117

116-
currentRuleResultsProperty().values().subscribe(this::highlightXPathResults);
117-
currentErrorNodesProperty().values().subscribe(this::highlightErrorNodes);
118-
currentRelatedNodesProperty().values().subscribe(this::highlightRelatedNodes);
118+
currentRuleResultsProperty().values().map(this::highlightXPathResults).subscribe(this::updateStyling);
119+
currentErrorNodesProperty().values().map(this::highlightErrorNodes).subscribe(this::updateStyling);
120+
currentRelatedNodesProperty().values().map(this::highlightRelatedNodes).subscribe(this::updateStyling);
119121

120122
initNodeSelectionHandling(designerRoot, selectionEvts, true);
121123

@@ -168,8 +170,8 @@ private void scrollToNode(Node node, boolean scrollToTop) {
168170
try {
169171
visibleLength = lastVisibleParToAllParIndex() - firstVisibleParToAllParIndex();
170172
} catch (AssertionError e) {
171-
// may be thrown when many selection events occur in quick succession?
172-
// Something like, the paragraphs
173+
// May be thrown when many selection events occur in quick succession?
174+
// The error is "dead code", probably a corner case in RichTextFX
173175
return;
174176
}
175177

@@ -271,28 +273,40 @@ public Var<List<Node>> currentRelatedNodesProperty() {
271273
}
272274

273275

274-
/** Highlights xpath results (xpath highlight). */
275-
private void highlightXPathResults(Collection<? extends Node> nodes) {
276-
styleNodes(nodes, StyleLayerIds.XPATH_RESULT, true);
276+
/**
277+
* Highlights xpath results (xpath highlight).
278+
*/
279+
@Pure
280+
private RichRunnable highlightXPathResults(Collection<? extends Node> nodes) {
281+
return styleNodesUpdate(nodes, StyleLayerIds.XPATH_RESULT, true);
277282
}
278283

279284

280-
/** Highlights name occurrences (secondary highlight). */
281-
private void highlightRelatedNodes(Collection<? extends Node> occs) {
282-
styleNodes(occs, StyleLayerIds.NAME_OCCURRENCE, true);
285+
/**
286+
* Highlights name occurrences (secondary highlight).
287+
*/
288+
@Pure
289+
private RichRunnable highlightRelatedNodes(Collection<? extends Node> occs) {
290+
return styleNodesUpdate(occs, StyleLayerIds.NAME_OCCURRENCE, true);
283291
}
284292

285293

286-
/** Highlights nodes that are in error (secondary highlight). */
287-
private void highlightErrorNodes(Collection<? extends Node> nodes) {
288-
styleNodes(nodes, StyleLayerIds.ERROR, true);
294+
/**
295+
* Highlights nodes that are in error (secondary highlight).
296+
*/
297+
@Pure
298+
private RichRunnable highlightErrorNodes(Collection<? extends Node> nodes) {
299+
RichRunnable r = styleNodesUpdate(nodes, StyleLayerIds.ERROR, true);
289300
if (!nodes.isEmpty()) {
290-
scrollToNode(nodes.iterator().next(), true);
301+
r = r.andThen(() -> scrollToNode(nodes.iterator().next(), true));
291302
}
303+
return r;
292304
}
293305

294306

295-
/** Moves the caret to a position and makes the view follow it. */
307+
/**
308+
* Moves the caret to a position and makes the view follow it.
309+
*/
296310
public void moveCaret(int line, int column) {
297311
moveTo(line, column);
298312
requestFollowCaret();
@@ -301,32 +315,34 @@ public void moveCaret(int line, int column) {
301315

302316
@Override
303317
public void setFocusNode(final Node node, DataHolder options) {
318+
// editor must not be scrolled when finding a new selection in a
319+
// tree that is being edited
320+
if (node != null && !options.hasData(SELECTION_RECOVERY)) {
321+
// don't randomly jump to top of eg ClassOrInterfaceBody
322+
// when selecting from a caret position
323+
scrollToNode(node, !options.hasData(CARET_POSITION));
324+
}
304325

305326

306-
boolean changed = !Objects.equals(node, currentFocusNode.getValue());
307-
if (changed) {
327+
RichRunnable update = () -> {};
328+
if (Objects.equals(node, currentFocusNode.getValue())) {
329+
return;
330+
}
308331

309-
currentFocusNode.setValue(node);
332+
currentFocusNode.setValue(node);
310333

311-
// editor is only restyled if the selection has changed
312-
Platform.runLater(() -> styleNodes(
313-
node == null ? emptyList() : singleton(node), StyleLayerIds.FOCUS, true));
334+
// editor is only restyled if the selection has changed
335+
update = update.andThen(styleNodesUpdate(
336+
node == null ? emptyList() : singleton(node), StyleLayerIds.FOCUS, true));
314337

315-
if (node == null) {
316-
highlightRelatedNodes(emptyList());
317-
} else {
318-
Platform.runLater(() -> highlightRelatedNodes(relatedNodesSelector.getValue().getHighlightedNodesWhenSelecting(node)));
319-
}
338+
if (node == null) {
339+
update = update.andThen(highlightRelatedNodes(emptyList()));
340+
} else {
341+
update = update.andThen(highlightRelatedNodes(relatedNodesSelector.getValue().getHighlightedNodesWhenSelecting(node)));
320342
}
321343

322-
// editor must not be scrolled when finding a new selection in a
323-
// tree that is being edited
324-
// Editor must be scrolled after styling
325-
if (node != null && !options.hasData(SELECTION_RECOVERY)) {
326-
// don't randomly jump to top of eg ClassOrInterfaceBody
327-
// when selecting from a caret position
328-
Platform.runLater(() -> scrollToNode(node, !options.hasData(CARET_POSITION)));
329-
}
344+
Runnable finalUpdate = update;
345+
Platform.runLater(() -> updateStyling(finalUpdate));
330346
}
331347

332348

0 commit comments

Comments
 (0)