mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
more robust heuristic: compare text when deciding whether two nodes worth merging
This commit is contained in:
+6
-22
@@ -19,6 +19,7 @@ import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInsight.CodeInsightTestCase;
|
||||
import com.intellij.ide.highlighter.HtmlFileType;
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
import com.intellij.ide.highlighter.XmlFileType;
|
||||
import com.intellij.lang.FileASTNode;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
@@ -514,26 +515,12 @@ public class SmartPsiElementPointersTest extends CodeInsightTestCase {
|
||||
}
|
||||
|
||||
public void testInXml2() {
|
||||
final PsiFile file = configureByText(HtmlFileType.INSTANCE,
|
||||
"<!DOCTYPE html>\n" +
|
||||
final PsiFile file = configureByText(XmlFileType.INSTANCE,
|
||||
"<html>\n" +
|
||||
"<head>\n" +
|
||||
" <title></title>\n" +
|
||||
"</head>\n" +
|
||||
"<body>\n" +
|
||||
"<div class=\"cls\">\n" +
|
||||
" <ul class=\"dropdown-menu\">\n" +
|
||||
" <li><a href=\"#\">Action</a></li>\n" +
|
||||
" <li><a href=\"#\">Another action</a></li>\n" +
|
||||
" <li><a href=\"#\">Something else here</a></li>\n" +
|
||||
" <li class=\"divider\"></li>\n" +
|
||||
" <li class=\"dropdown-header\">Nav header</li>\n" +
|
||||
" <li><a href=\"#\">Separated link</a></li>\n" +
|
||||
" <li><a href=\"#\">One more separated link</a></li>\n" +
|
||||
" </ul>\n" +
|
||||
"<caret>\n" +
|
||||
"</div>\n" +
|
||||
"</body>\n" +
|
||||
"</html>"
|
||||
);
|
||||
|
||||
@@ -542,17 +529,14 @@ public class SmartPsiElementPointersTest extends CodeInsightTestCase {
|
||||
assertEquals("ul", ul.getName());
|
||||
assertEquals("dropdown-menu", ul.getAttributeValue("class"));
|
||||
|
||||
final SmartPsiElementPointer<XmlTag> ulPointer = SmartPointerManager.getInstance(getProject()).createSmartPsiElementPointer(
|
||||
ul);
|
||||
SmartPsiElementPointer<XmlTag> ulPointer = SmartPointerManager.getInstance(getProject()).createSmartPsiElementPointer(ul);
|
||||
|
||||
WriteCommandAction.runWriteCommandAction(getProject(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getEditor().getDocument().insertString(getEditor().getCaretModel().getOffset(), " <ul class=\"nav navbar-nav navbar-right\">\n" +
|
||||
" <li><a href=\"../navbar/\">Default</a></li>\n" +
|
||||
" <li class=\"active\"><a href=\"./\">Static top</a></li>\n" +
|
||||
" <li><a href=\"../navbar-fixed-top/\">Fixed top</a></li>\n" +
|
||||
" </ul>\n");
|
||||
int offset = getEditor().getCaretModel().getOffset();
|
||||
getEditor().getDocument().insertString(offset, " <ul class=\"nav navbar-nav navbar-right\">\n" +
|
||||
" </ul>\n");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -83,8 +83,8 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
private CharTable myCharTable;
|
||||
private final CharSequence myText;
|
||||
private final char[] myTextArray;
|
||||
private boolean myDebugMode = false;
|
||||
private int myLexemeCount = 0;
|
||||
private boolean myDebugMode;
|
||||
private int myLexemeCount;
|
||||
private boolean myTokenTypeChecked;
|
||||
private ITokenTypeRemapper myRemapper;
|
||||
private WhitespaceSkippedCallback myWhitespaceSkippedCallback;
|
||||
@@ -93,7 +93,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
private final MyTreeStructure myParentLightTree;
|
||||
private final int myOffset;
|
||||
|
||||
private Map<Key, Object> myUserData = null;
|
||||
private Map<Key, Object> myUserData;
|
||||
private IElementType myCachedTokenType;
|
||||
|
||||
private final LimitedPool<StartMarker> START_MARKERS = new LimitedPool<StartMarker>(2000, new LimitedPool.ObjectFactory<StartMarker>() {
|
||||
@@ -635,7 +635,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
|
||||
private static class DoneMarker extends ProductionMarker {
|
||||
private StartMarker myStart;
|
||||
private boolean myCollapse = false;
|
||||
private boolean myCollapse;
|
||||
|
||||
public DoneMarker() {
|
||||
myEdgeTokenBinder = WhitespacesBinders.DEFAULT_RIGHT_BINDER;
|
||||
@@ -916,7 +916,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
return myCurrentLexeme >= myLexemeCount;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"SuspiciousMethodCalls"})
|
||||
@SuppressWarnings("SuspiciousMethodCalls")
|
||||
private void rollbackTo(Marker marker) {
|
||||
myCurrentLexeme = ((StartMarker)marker).myLexemeIndex;
|
||||
myTokenTypeChecked = true;
|
||||
@@ -948,7 +948,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"SuspiciousMethodCalls"})
|
||||
@SuppressWarnings("SuspiciousMethodCalls")
|
||||
public void drop(Marker marker) {
|
||||
final DoneMarker doneMarker = ((StartMarker)marker).myDoneMarker;
|
||||
if (doneMarker != null) {
|
||||
@@ -973,7 +973,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
myProduction.add(doneMarker);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"SuspiciousMethodCalls"})
|
||||
@SuppressWarnings("SuspiciousMethodCalls")
|
||||
public void errorBefore(Marker marker, String message, Marker before) {
|
||||
doValidityChecks(marker, before);
|
||||
|
||||
@@ -1001,7 +1001,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
myProduction.add(doneMarker);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"SuspiciousMethodCalls"})
|
||||
@SuppressWarnings("SuspiciousMethodCalls")
|
||||
public void doneBefore(Marker marker, Marker before) {
|
||||
doValidityChecks(marker, before);
|
||||
|
||||
@@ -1173,9 +1173,9 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
@NotNull
|
||||
private DiffLog merge(@NotNull final ASTNode oldRoot, @NotNull StartMarker newRoot) {
|
||||
DiffLog diffLog = new DiffLog();
|
||||
final ConvertFromTokensToASTBuilder builder = new ConvertFromTokensToASTBuilder(newRoot, diffLog);
|
||||
final MyTreeStructure treeStructure = new MyTreeStructure(newRoot, null);
|
||||
final MyComparator comparator = new MyComparator(getUserDataUnprotected(CUSTOM_COMPARATOR), treeStructure);
|
||||
DiffTreeChangeBuilder<ASTNode, LighterASTNode> builder = new ConvertFromTokensToASTBuilder(newRoot, diffLog);
|
||||
MyTreeStructure treeStructure = new MyTreeStructure(newRoot, null);
|
||||
ShallowNodeComparator<ASTNode, LighterASTNode> comparator = new MyComparator(getUserDataUnprotected(CUSTOM_COMPARATOR), treeStructure);
|
||||
|
||||
final ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator();
|
||||
BlockSupportImpl.diffTrees(oldRoot, builder, comparator, treeStructure, indicator == null ? new EmptyProgressIndicator() : indicator);
|
||||
@@ -1197,7 +1197,9 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
final Stack<StartMarker> nodes = ContainerUtil.newStack();
|
||||
nodes.push(rootMarker);
|
||||
|
||||
@SuppressWarnings({"MultipleVariablesInDeclaration"}) int lastErrorIndex = -1, maxDepth = 0, curDepth = 0;
|
||||
int lastErrorIndex = -1;
|
||||
int maxDepth = 0;
|
||||
int curDepth = 0;
|
||||
for (int i = 1; i < myProduction.size(); i++) {
|
||||
final ProductionMarker item = myProduction.get(i);
|
||||
|
||||
@@ -1692,7 +1694,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
private void insertLeaf(@NotNull IElementType type, @NotNull PsiBuilderImpl builder, int startLexemeIndex, int endLexemeIndex) {
|
||||
final int start = builder.myLexStarts[startLexemeIndex];
|
||||
final int end = builder.myLexStarts[endLexemeIndex];
|
||||
if (start > end || ((start == end) && !(type instanceof ILeafElementType))) return;
|
||||
if (start > end || start == end && !(type instanceof ILeafElementType)) return;
|
||||
|
||||
final Token lexeme;
|
||||
if (type instanceof ILightLazyParseableElementType) {
|
||||
@@ -1712,6 +1714,12 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
|
||||
ensureCapacity();
|
||||
nodes[count++] = lexeme;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CharSequence toString(@NotNull LighterASTNode node) {
|
||||
return myRoot.myBuilder.myText.subSequence(node.getStartOffset(), node.getEndOffset());
|
||||
}
|
||||
}
|
||||
|
||||
private static class ASTConverter implements Convertor<Node, ASTNode> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -77,4 +77,10 @@ public class ASTStructure implements FlyweightCapableTreeStructure<ASTNode> {
|
||||
@Override
|
||||
public void disposeChildren(final ASTNode[] nodes, final int count) {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CharSequence toString(@NotNull ASTNode node) {
|
||||
return node.getChars();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.intellij.util.diff;
|
||||
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ThreeState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -47,7 +48,7 @@ public class DiffTree<OT, NT> {
|
||||
@NotNull ShallowNodeComparator<OT, NT> comparator,
|
||||
@NotNull DiffTreeChangeBuilder<OT, NT> consumer) {
|
||||
final DiffTree<OT, NT> tree = new DiffTree<OT, NT>(oldTree, newTree, comparator);
|
||||
tree.build(oldTree.getRoot(), newTree.getRoot(), 0, Integer.MAX_VALUE, consumer);
|
||||
tree.build(oldTree.getRoot(), newTree.getRoot(), 0, consumer);
|
||||
}
|
||||
|
||||
private enum CompareResult {
|
||||
@@ -80,9 +81,7 @@ public class DiffTree<OT, NT> {
|
||||
};
|
||||
|
||||
@NotNull
|
||||
private CompareResult build(@NotNull OT oldN, @NotNull NT newN, int level, int maxLevel, @NotNull DiffTreeChangeBuilder<OT, NT> consumer) {
|
||||
if (level == maxLevel) return CompareResult.NOT_EQUAL; // too deep, abort
|
||||
|
||||
private CompareResult build(@NotNull OT oldN, @NotNull NT newN, int level, @NotNull DiffTreeChangeBuilder<OT, NT> consumer) {
|
||||
OT oldNode = myOldTree.prepareForGetChildren(oldN);
|
||||
NT newNode = myNewTree.prepareForGetChildren(newN);
|
||||
|
||||
@@ -117,14 +116,13 @@ public class DiffTree<OT, NT> {
|
||||
final ShallowNodeComparator<OT, NT> comparator = myComparator;
|
||||
|
||||
int minSize = Math.min(oldChildrenSize, newChildrenSize);
|
||||
int newMaxLevel = Math.min(maxLevel, level+4); // try not to descend recursively too deep
|
||||
int suffixLength = match(oldChildren, oldChildrenSize - 1, newChildren, newChildrenSize - 1, level, -1, minSize, newMaxLevel);
|
||||
int prefixLength = oldChildrenSize == 1 && newChildrenSize == 1 ? 0 : match(oldChildren, 0, newChildren, 0, level, 1, minSize-suffixLength, newMaxLevel);
|
||||
int suffixLength = match(oldChildren, oldChildrenSize - 1, newChildren, newChildrenSize - 1, level, -1, minSize);
|
||||
int prefixLength = oldChildrenSize == 1 && newChildrenSize == 1 ? 0 : match(oldChildren, 0, newChildren, 0, level, 1, minSize-suffixLength);
|
||||
|
||||
if (oldChildrenSize == newChildrenSize && suffixLength + prefixLength == oldChildrenSize) {
|
||||
result = CompareResult.EQUAL;
|
||||
}
|
||||
else if (consumer == emptyConsumer()){
|
||||
else if (consumer == emptyConsumer()) {
|
||||
result = CompareResult.NOT_EQUAL;
|
||||
}
|
||||
else {
|
||||
@@ -141,7 +139,7 @@ public class DiffTree<OT, NT> {
|
||||
CompareResult c11 = looksEqual(comparator, oldChild1, newChild1);
|
||||
if (c11 == CompareResult.EQUAL || c11 == CompareResult.DRILL_DOWN_NEEDED) {
|
||||
if (c11 == CompareResult.DRILL_DOWN_NEEDED) {
|
||||
build(oldChild1, newChild1, level + 1, maxLevel, consumer);
|
||||
build(oldChild1, newChild1, level + 1, consumer);
|
||||
}
|
||||
oldIndex--;
|
||||
newIndex--;
|
||||
@@ -225,7 +223,7 @@ public class DiffTree<OT, NT> {
|
||||
CompareResult c = oldFirstChild == null || newFirstChild == null ? CompareResult.NOT_EQUAL : looksEqual(comparator, oldFirstChild, newFirstChild);
|
||||
if (c == CompareResult.EQUAL || c == CompareResult.TYPE_ONLY || c == CompareResult.DRILL_DOWN_NEEDED) {
|
||||
if (c == CompareResult.DRILL_DOWN_NEEDED) {
|
||||
build(oldFirstChild, newFirstChild, level + 1, maxLevel, consumer);
|
||||
build(oldFirstChild, newFirstChild, level + 1, consumer);
|
||||
}
|
||||
else {
|
||||
consumer.nodeReplaced(oldFirstChild, newFirstChild);
|
||||
@@ -254,8 +252,7 @@ public class DiffTree<OT, NT> {
|
||||
int newIndex,
|
||||
int level,
|
||||
int step, // 1 if we go from the start to the end; -1 if we go from the end to the start
|
||||
int maxLength,
|
||||
int maxLevel) {
|
||||
int maxLength) {
|
||||
int delta = 0;
|
||||
while (delta != maxLength*step) {
|
||||
OT oldChild = oldChildren[oldIndex + delta];
|
||||
@@ -264,7 +261,11 @@ public class DiffTree<OT, NT> {
|
||||
CompareResult c11 = looksEqual(myComparator, oldChild, newChild);
|
||||
|
||||
if (c11 == CompareResult.DRILL_DOWN_NEEDED) {
|
||||
c11 = build(oldChild, newChild, level + 1, maxLevel, DiffTree.<OT, NT>emptyConsumer());
|
||||
CharSequence oldText = myOldTree.toString(oldChild);
|
||||
CharSequence newText = myNewTree.toString(newChild);
|
||||
c11 = StringUtil.equals(oldText, newText)
|
||||
? build(oldChild, newChild, level + 1, DiffTree.<OT, NT>emptyConsumer())
|
||||
: CompareResult.NOT_EQUAL;
|
||||
}
|
||||
assert c11 != CompareResult.DRILL_DOWN_NEEDED;
|
||||
if (c11 != CompareResult.EQUAL) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,4 +36,7 @@ public interface FlyweightCapableTreeStructure<T> {
|
||||
int getChildren(@NotNull T parent, @NotNull Ref<T[]> into);
|
||||
|
||||
void disposeChildren(T[] nodes, int count);
|
||||
|
||||
@NotNull
|
||||
CharSequence toString(@NotNull T node);
|
||||
}
|
||||
|
||||
@@ -92,6 +92,12 @@ public class DiffTreeTest extends TestCase {
|
||||
@Override
|
||||
public void disposeChildren(final Node[] nodes, final int count) {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CharSequence toString(@NotNull Node node) {
|
||||
return node.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static class NodeComparator implements ShallowNodeComparator<Node, Node> {
|
||||
|
||||
Reference in New Issue
Block a user