PSI events: sort by element index which is more robust than current/initial offset

This commit is contained in:
peter
2018-01-02 22:32:06 +01:00
parent ec69852fc7
commit ee37e819ab
3 changed files with 51 additions and 7 deletions
@@ -145,8 +145,7 @@ public class TreeChangeEventImpl implements TreeChangeEvent{
}
public void fireEvents() {
Collection<TreeChangeImpl> changes = ContainerUtil.sorted(myChangedElements.values(),
Comparator.comparing(TreeChangeImpl::getInitialStart));
Collection<TreeChangeImpl> changes = ContainerUtil.sorted(myChangedElements.values());
for (TreeChangeImpl change : changes) {
change.fireEvents((PsiFile)myFileElement.getPsi());
}
@@ -22,24 +22,23 @@ import com.intellij.pom.tree.events.TreeChange;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.tree.CompositeElement;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.JBIterable;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class TreeChangeImpl implements TreeChange {
public class TreeChangeImpl implements TreeChange, Comparable<TreeChangeImpl> {
private final CompositeElement myParent;
private final List<CompositeElement> mySuperParents;
private final LinkedHashSet<TreeElement> myInitialChildren = new LinkedHashSet<>();
private final Map<TreeElement, Integer> myInitialLengths = new HashMap<>();
private final Set<TreeElement> myContentChangeChildren = new HashSet<>();
private final int myInitialStart;
private Map<TreeElement, ChangeInfoImpl> myChanges;
public TreeChangeImpl(@NotNull CompositeElement parent) {
myParent = parent;
mySuperParents = JBIterable.generate(parent.getTreeParent(), TreeElement::getTreeParent).toList();
myInitialStart = myParent.getStartOffset();
for (TreeElement child : getCurrentChildren()) {
myInitialChildren.add(child);
myInitialLengths.put(child, child.getTextLength());
@@ -55,8 +54,29 @@ public class TreeChangeImpl implements TreeChange {
return JBIterable.generate(myParent.getFirstChildNode(), TreeElement::getTreeNext);
}
int getInitialStart() {
return myInitialStart;
@Override
public int compareTo(@NotNull TreeChangeImpl o) {
List<CompositeElement> thisParents = getSuperParents();
List<CompositeElement> thatParents = o.getSuperParents();
for (int i = 1; i <= thisParents.size() && i <= thatParents.size(); i++) {
CompositeElement thisParent = i < thisParents.size() ? thisParents.get(thisParents.size() - i) : myParent;
CompositeElement thatParent = i < thatParents.size() ? thatParents.get(thatParents.size() - i) : o.myParent;
int result = compareNodePositions(thisParent, thatParent);
if (result != 0) return result;
}
return 0;
}
private static int compareNodePositions(CompositeElement node1, CompositeElement node2) {
if (node1 == node2) return 0;
int o1 = node1.getStartOffsetInParent();
int o2 = node2.getStartOffsetInParent();
return o1 != o2 ? Integer.compare(o1, o2) : Integer.compare(getChildIndex(node1), getChildIndex(node2));
}
private static int getChildIndex(CompositeElement e) {
return ArrayUtil.indexOf(e.getTreeParent().getChildren(null), e);
}
int getLengthDelta() {
@@ -78,6 +78,31 @@ class PsiEventConsistencyTest : LightPlatformCodeInsightFixtureTestCase() {
assertEquals(root.text, file.viewProvider.document!!.text)
}
}
fun `test changes in different AST parents at the same initial offset`() {
val file = createEmptyFile()
WriteCommandAction.runWriteCommandAction(project) {
//prepare
val root = file.node as FileElement
root.replaceChild(root.firstChildNode, composite(compositeTypes[0],
composite(compositeTypes[0]),
composite(compositeTypes[0], leaf(leafTypes[0], "x")),
composite(compositeTypes[0], leaf(leafTypes[0], "s"))))
ChangeUtil.prepareAndRunChangeAction(ChangeUtil.ChangeAction {
val pComposite = root.firstChildNode.firstChildNode
val xComposite = pComposite.treeNext
val sComposite = xComposite.treeNext
sComposite.replaceChild(sComposite.firstChildNode, leaf(leafTypes[0], "h"))
ChangeUtil.prepareAndRunChangeAction(ChangeUtil.ChangeAction {
pComposite.addChild(leaf(leafTypes[0], "p"))
xComposite.removeChild(xComposite.firstChildNode)
}, root)
}, root)
assertEquals("ph", root.text)
assertEquals(root.text, file.viewProvider.document!!.text)
}
}
fun testPsiDocSynchronization() {
PropertyChecker.forAll(commands).shouldHold { cmd ->