Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paragraph list trim fix #795

Merged
merged 3 commits into from
Feb 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package org.fxmisc.richtext.api;

import org.fxmisc.richtext.InlineCssTextAreaAppTest;
import org.fxmisc.richtext.model.Paragraph;
import org.junit.Test;
import org.junit.Assert;
import org.reactfx.collection.LiveList;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

public class VisibleParagraphTest extends InlineCssTextAreaAppTest {

@Test
public void get_first_visible_paragraph_index_with_non_blank_lines() {
String[] lines = {
"abc",
"def",
"ghi"
};
interact(() -> {
area.setWrapText(true);
stage.setWidth(120);
area.replaceText(String.join("\n", lines));
});
assertEquals(0, area.firstVisibleParToAllParIndex());
assertEquals(2, area.lastVisibleParToAllParIndex());
assertEquals(1, area.visibleParToAllParIndex(1));
}
@Test
public void get_last_visible_paragraph_index_with_non_blank_lines() {
String[] lines = {
"abc",
"def",
"ghi"
};
interact(() -> {
area.setWrapText(true);
stage.setWidth(120);
area.replaceText(String.join("\n", lines));
});
assertEquals(2, area.lastVisibleParToAllParIndex());
}
@Test
public void get_specific_visible_paragraph_index_with_non_blank_lines() {
String[] lines = {
"abc",
"def",
"ghi"
};
interact(() -> {
area.setWrapText(true);
stage.setWidth(120);
area.replaceText(String.join("\n", lines));
});
assertEquals(2, area.visibleParToAllParIndex(2));
}
@Test
public void get_first_visible_paragraph_index_with_all_blank_lines() {
String[] lines = {
"",
"",
""
};
interact(() -> {
area.setWrapText(true);
stage.setWidth(120);
area.replaceText(String.join("\n", lines));
});
assertEquals(0, area.firstVisibleParToAllParIndex());
}
@Test
public void get_last_visible_paragraph_index_with_all_blank_lines() {
String[] lines = {
"",
"",
""
};
interact(() -> {
area.setWrapText(true);
stage.setWidth(120);
area.replaceText(String.join("\n", lines));
});
assertEquals(2, area.lastVisibleParToAllParIndex());
}
@Test
public void get_specific_visible_paragraph_index_with_all_blank_lines() {
String[] lines = {
"",
"",
""
};
interact(() -> {
area.setWrapText(true);
stage.setWidth(120);
area.replaceText(String.join("\n", lines));
});
assertEquals(2, area.visibleParToAllParIndex(2));
}

@Test
public void get_first_visible_paragraph_index_with_some_blank_lines() {
String[] lines = {
"abc",
"",
"",
"",
"def",
""
};
interact(() -> {
area.setWrapText(true);
stage.setWidth(120);
area.replaceText(String.join("\n", lines));
});
assertEquals(0, area.firstVisibleParToAllParIndex());
}
@Test
public void get_last_visible_paragraph_index_with_some_blank_lines() {
String[] lines = {
"abc",
"",
"",
"",
"def",
""
};
interact(() -> {
area.setWrapText(true);
stage.setWidth(120);
area.replaceText(String.join("\n", lines));
});
assertEquals(5, area.lastVisibleParToAllParIndex());
}
@Test
public void get_specific_visible_paragraph_index_with_some_blank_lines() {
String[] lines = {
"abc",
"",
"",
"",
"def",
""
};
interact(() -> {
area.setWrapText(true);
stage.setWidth(120);
area.replaceText(String.join("\n", lines));
});
assertEquals(3, area.visibleParToAllParIndex(3));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;

import org.reactfx.EventSource;
import org.reactfx.EventStream;
Expand All @@ -19,6 +20,8 @@
import org.reactfx.collection.QuasiListModification;
import org.reactfx.collection.SuspendableList;
import org.reactfx.collection.UnmodifiableByDefaultLiveList;
import org.reactfx.util.Tuple2;
import org.reactfx.util.Tuples;
import org.reactfx.value.SuspendableVal;
import org.reactfx.value.Val;

Expand All @@ -43,7 +46,7 @@ protected Subscription observeInputs() {
return parChangesList.subscribe(list -> {
ListChangeAccumulator<Paragraph<PS, SEG, S>> accumulator = new ListChangeAccumulator<>();
for (MaterializedListModification<Paragraph<PS, SEG, S>> mod : list) {
mod = mod.trim();
mod = trim(mod);

// add the quasiListModification itself, not as a quasiListChange, in case some overlap
accumulator.add(QuasiListModification.create(mod.getFrom(), mod.getRemoved(), mod.getAddedSize()));
Expand Down Expand Up @@ -217,4 +220,73 @@ private void updateMulti(
parChangesList.push(parChanges);
});
}

/**
* Copy of org.reactfx.collection.MaterializedListModification.trim()
* that uses reference comparison instead of equals().
*/
private MaterializedListModification<Paragraph<PS, SEG, S>> trim(MaterializedListModification<Paragraph<PS, SEG, S>> mod) {
return commonPrefixSuffixLengths(mod.getRemoved(), mod.getAdded()).map((pref, suff) -> {
if(pref == 0 && suff == 0) {
return mod;
} else {
return MaterializedListModification.create(
mod.getFrom() + pref,
mod.getRemoved().subList(pref, mod.getRemovedSize() - suff),
mod.getAdded().subList(pref, mod.getAddedSize() - suff));
}
});
}

/**
* Copy of org.reactfx.util.Lists.commonPrefixSuffixLengths()
* that uses reference comparison instead of equals().
*/
private static Tuple2<Integer, Integer> commonPrefixSuffixLengths(List<?> l1, List<?> l2) {
int n1 = l1.size();
int n2 = l2.size();

if(n1 == 0 || n2 == 0) {
return Tuples.t(0, 0);
}

int pref = commonPrefixLength(l1, l2);
if(pref == n1 || pref == n2) {
return Tuples.t(pref, 0);
}

int suff = commonSuffixLength(l1, l2);

return Tuples.t(pref, suff);
}

/**
* Copy of org.reactfx.util.Lists.commonPrefixLength()
* that uses reference comparison instead of equals().
*/
private static int commonPrefixLength(List<?> l, List<?> m) {
ListIterator<?> i = l.listIterator();
ListIterator<?> j = m.listIterator();
while(i.hasNext() && j.hasNext()) {
if(i.next() != j.next()) {
return i.nextIndex() - 1;
}
}
return i.nextIndex();
}

/**
* Copy of org.reactfx.util.Lists.commonSuffixLength()
* that uses reference comparison instead of equals().
*/
private static int commonSuffixLength(List<?> l, List<?> m) {
ListIterator<?> i = l.listIterator(l.size());
ListIterator<?> j = m.listIterator(m.size());
while(i.hasPrevious() && j.hasPrevious()) {
if(i.previous() != j.previous()) {
return l.size() - i.nextIndex() - 1;
}
}
return l.size() - i.nextIndex();
}
}