Skip to content

Commit 042ede5

Browse files
Merge pull request #721 from JordanMartinez/fixMultiPlainChanges
Prevent `multiPlainChanges()` from emitting an empty list
2 parents 3e13d21 + c8bdf5a commit 042ede5

5 files changed

Lines changed: 47 additions & 13 deletions

File tree

richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywords.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.fxmisc.richtext.demo;
22

3+
import java.time.Duration;
34
import java.util.Collection;
45
import java.util.Collections;
56
import java.util.regex.Matcher;
@@ -15,6 +16,7 @@
1516
import org.fxmisc.richtext.LineNumberFactory;
1617
import org.fxmisc.richtext.model.StyleSpans;
1718
import org.fxmisc.richtext.model.StyleSpansBuilder;
19+
import org.reactfx.Subscription;
1820

1921
public class JavaKeywords extends Application {
2022

@@ -80,13 +82,27 @@ public static void main(String[] args) {
8082
@Override
8183
public void start(Stage primaryStage) {
8284
CodeArea codeArea = new CodeArea();
85+
86+
// add line numbers to the left of area
8387
codeArea.setParagraphGraphicFactory(LineNumberFactory.get(codeArea));
8488

85-
codeArea.richChanges()
86-
.filter(ch -> !ch.getInserted().equals(ch.getRemoved())) // XXX
87-
.subscribe(change -> {
88-
codeArea.setStyleSpans(0, computeHighlighting(codeArea.getText()));
89-
});
89+
// recompute the syntax highlighting 500 ms after user stops editing area
90+
Subscription cleanupWhenNoLongerNeedIt = codeArea
91+
92+
// plain changes = ignore style changes that are emitted when syntax highlighting is reapplied
93+
// multi plain changes = save computation by not rerunning the code multiple times
94+
// when making multiple changes (e.g. renaming a method at multiple parts in file)
95+
.multiPlainChanges()
96+
97+
// do not emit an event until 500 ms have passed since the last emission of previous stream
98+
.successionEnds(Duration.ofMillis(500))
99+
100+
// run the following code block when previous stream emits an event
101+
.subscribe(ignore -> codeArea.setStyleSpans(0, computeHighlighting(codeArea.getText())));
102+
103+
// when no longer need syntax highlighting and wish to clean up memory leaks
104+
// run: `cleanupWhenNoLongerNeedIt.unsubscribe();`
105+
90106
codeArea.replaceText(0, 0, sampleCode);
91107

92108
Scene scene = new Scene(new StackPane(new VirtualizedScrollPane<>(codeArea)), 600, 400);

richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsAsync.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.fxmisc.richtext.LineNumberFactory;
2121
import org.fxmisc.richtext.model.StyleSpans;
2222
import org.fxmisc.richtext.model.StyleSpansBuilder;
23+
import org.reactfx.Subscription;
2324

2425
public class JavaKeywordsAsync extends Application {
2526

@@ -90,11 +91,10 @@ public void start(Stage primaryStage) {
9091
executor = Executors.newSingleThreadExecutor();
9192
codeArea = new CodeArea();
9293
codeArea.setParagraphGraphicFactory(LineNumberFactory.get(codeArea));
93-
codeArea.richChanges()
94-
.filter(ch -> !ch.getInserted().equals(ch.getRemoved())) // XXX
94+
Subscription cleanupWhenDone = codeArea.multiPlainChanges()
9595
.successionEnds(Duration.ofMillis(500))
9696
.supplyTask(this::computeHighlightingAsync)
97-
.awaitLatest(codeArea.richChanges())
97+
.awaitLatest(codeArea.multiPlainChanges())
9898
.filterMap(t -> {
9999
if(t.isSuccess()) {
100100
return Optional.of(t.get());
@@ -104,6 +104,9 @@ public void start(Stage primaryStage) {
104104
}
105105
})
106106
.subscribe(this::applyHighlighting);
107+
108+
// call when no longer need it: `cleanupWhenFinished.unsubscribe();`
109+
107110
codeArea.replaceText(0, 0, sampleCode);
108111

109112
Scene scene = new Scene(new StackPane(new VirtualizedScrollPane<>(codeArea)), 600, 400);

richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/SpellChecking.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.io.InputStream;
66
import java.io.InputStreamReader;
77
import java.text.BreakIterator;
8+
import java.time.Duration;
89
import java.util.Collection;
910
import java.util.Collections;
1011
import java.util.HashSet;
@@ -19,6 +20,7 @@
1920
import javafx.scene.Scene;
2021
import javafx.scene.layout.StackPane;
2122
import javafx.stage.Stage;
23+
import org.reactfx.Subscription;
2224

2325
public class SpellChecking extends Application {
2426

@@ -33,12 +35,14 @@ public void start(Stage primaryStage) {
3335
StyleClassedTextArea textArea = new StyleClassedTextArea();
3436
textArea.setWrapText(true);
3537

36-
textArea.richChanges()
37-
.filter(ch -> !ch.getInserted().equals(ch.getRemoved())) // XXX
38+
Subscription cleanupWhenFinished = textArea.multiPlainChanges()
39+
.successionEnds(Duration.ofMillis(500))
3840
.subscribe(change -> {
3941
textArea.setStyleSpans(0, computeHighlighting(textArea.getText()));
4042
});
4143

44+
// call when no longer need it: `cleanupWhenFinished.unsubscribe();`
45+
4246
// load the dictionary
4347
try (InputStream input = getClass().getResourceAsStream("spellchecking.dict");
4448
BufferedReader br = new BufferedReader(new InputStreamReader(input))) {

richtextfx/src/main/java/org/fxmisc/richtext/model/EditableStyledDocument.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,14 @@ public interface EditableStyledDocument<PS, SEG, S> extends StyledDocument<PS, S
6767
*/
6868
default EventStream<List<PlainTextChange>> multiPlainChanges() {
6969
return multiRichChanges()
70+
// map to a List<PlainTextChange>
7071
.map(list -> Arrays.asList(list.stream()
71-
.map(RichTextChange::toPlainTextChange)
7272
// filter out rich changes where the style was changed but text wasn't added/removed
73-
.filter(pc -> !pc.isIdentity())
74-
.toArray(PlainTextChange[]::new)));
73+
.filter(rtc -> !rtc.isPlainTextIdentity())
74+
.map(RichTextChange::toPlainTextChange)
75+
.toArray(PlainTextChange[]::new)))
76+
// only emit non-empty lists
77+
.filter(list -> !list.isEmpty());
7578
}
7679

7780
/**

richtextfx/src/main/java/org/fxmisc/richtext/model/RichTextChange.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,12 @@ protected final RichTextChange<PS, SEG, S> create(int position, StyledDocument<P
3737
public final PlainTextChange toPlainTextChange() {
3838
return new PlainTextChange(position, removed.getText(), inserted.getText());
3939
}
40+
41+
/**
42+
* Equivalent to {@code richChange.toPlainTextChange().isIdentity()} but without the additional object
43+
* creation via {@link #toPlainTextChange()}.
44+
*/
45+
public final boolean isPlainTextIdentity() {
46+
return removed.getText().equals(inserted.getText());
47+
}
4048
}

0 commit comments

Comments
 (0)