Skip to content

Commit 0256049

Browse files
Merge pull request #458 from JordanMartinez/undo-fix
Fixes Illegal Argument Exception due to merging an insertion change with a deletion change
2 parents e0bbed1 + b1f0f7b commit 0256049

File tree

2 files changed

+54
-24
lines changed

2 files changed

+54
-24
lines changed

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

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,33 @@
55

66
public abstract class TextChange<S, Self extends TextChange<S, Self>> {
77

8+
public static enum ChangeType {
9+
/** Indicates that the change will insert something but not remove anything */
10+
INSERTION,
11+
/** Indicates that the change will delete something but not insert anything */
12+
DELETION,
13+
/** Indicates that the change will remove something and insert something as its replacement */
14+
REPLACEMENT
15+
}
16+
17+
private ChangeType type;
18+
public final ChangeType getType() {
19+
if (type == null) {
20+
if (insertedLength() == 0) {
21+
if (removedLength() == 0) {
22+
throw new IllegalStateException("Cannot get the type of a change that neither inserts nor deletes anything.");
23+
} else {
24+
type = ChangeType.DELETION;
25+
}
26+
} else if (removedLength() == 0) {
27+
type = ChangeType.INSERTION;
28+
} else {
29+
type = ChangeType.REPLACEMENT;
30+
}
31+
}
32+
return type;
33+
}
34+
835
protected final int position;
936
protected final S removed;
1037
protected final S inserted;
@@ -29,35 +56,21 @@ public TextChange(int position, S removed, S inserted) {
2956
protected abstract Self create(int position, S removed, S inserted);
3057

3158
/**
32-
* Merges this change with the given change, if possible.
33-
* This change is considered to be the former and the given
34-
* change is considered to be the latter.
35-
* Changes can be merged if either
36-
* <ul>
37-
* <li>the latter's start matches the former's added text end; or</li>
38-
* <li>the latter's removed text end matches the former's added text end.</li>
39-
* </ul>
59+
* Merges this change with the given change only if the end of this change's inserted text
60+
* equals the latter's position and both are either insertion or deletion changes.
61+
*
4062
* @param latter change to merge with this change.
4163
* @return a new merged change if changes can be merged,
4264
* {@code null} otherwise.
4365
*/
4466
public Optional<Self> mergeWith(Self latter) {
45-
if(latter.position == this.position + this.insertedLength()) {
67+
if(this.getType() != ChangeType.REPLACEMENT
68+
&& this.getType() == latter.getType()
69+
&& this.getInsertionEnd() == latter.position) {
4670
S removedText = concat(this.removed, latter.removed);
4771
S addedText = concat(this.inserted, latter.inserted);
4872
return Optional.of(create(this.position, removedText, addedText));
49-
}
50-
else if(latter.position + latter.removedLength() == this.position + this.insertedLength()) {
51-
if(this.position <= latter.position) {
52-
S addedText = concat(sub(this.inserted, 0, latter.position - this.position), latter.inserted);
53-
return Optional.of(create(this.position, this.removed, addedText));
54-
}
55-
else {
56-
S removedText = concat(sub(latter.removed, 0, this.position - latter.position), this.removed);
57-
return Optional.of(create(latter.position, removedText, latter.inserted));
58-
}
59-
}
60-
else {
73+
} else {
6174
return Optional.empty();
6275
}
6376
}
@@ -83,9 +96,10 @@ public int hashCode() {
8396
public final String toString() {
8497
return
8598
this.getClass().getSimpleName() + "{\n" +
86-
"\tposition: " + position + "\n" +
87-
"\tremoved: " + removed + "\n" +
88-
"\tinserted: " + inserted + "\n" +
99+
"\tposition: " + position + "\n" +
100+
"\ttype: " + getType() + "\n" +
101+
"\tremoved: " + removed + "\n" +
102+
"\tinserted: " + inserted + "\n" +
89103
"}";
90104
}
91105
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.fxmisc.richtext.model;
2+
3+
import org.fxmisc.richtext.InlineCssTextArea;
4+
import org.junit.Test;
5+
6+
public class AreaTest {
7+
8+
private InlineCssTextArea area = new InlineCssTextArea();
9+
10+
@Test
11+
public void deletingTextThatWasJustInsertedShouldNotMergeTheTwoChanges() {
12+
area.replaceText(0, 0, "text");
13+
area.replaceText(0, area.getLength(), "");
14+
area.undo();
15+
}
16+
}

0 commit comments

Comments
 (0)