diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/CompositeElement.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/CompositeElement.java
index 01638a9fa2b6..b5ccb93c1e65 100644
--- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/CompositeElement.java
+++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/CompositeElement.java
@@ -673,23 +673,23 @@ public class CompositeElement extends TreeElement {
TreeUtil.ensureParsed(getFirstChildNode());
TreeUtil.ensureParsed(anotherParent.getFirstChildNode());
final ASTNode firstChild = anotherParent.getFirstChildNode();
- ChangeUtil.prepareAndRunChangeAction(new ChangeUtil.ChangeAction(){
- @Override
- public void makeChange(TreeChangeEvent destinationTreeChange) {
- remove((TreeChangeEventImpl)destinationTreeChange, (TreeElement)anotherParent.getFirstChildNode(), null);
- }
- }, (TreeElement)anotherParent);
+ ChangeUtil.prepareAndRunChangeAction(
+ event -> remove((TreeChangeEventImpl)event, (TreeElement)anotherParent.getFirstChildNode(), null),
+ (TreeElement)anotherParent);
if (firstChild != null) {
- ChangeUtil.prepareAndRunChangeAction(new ChangeUtil.ChangeAction(){
- @Override
- public void makeChange(TreeChangeEvent destinationTreeChange) {
- TreeElement first = getFirstChildNode();
- remove((TreeChangeEventImpl)destinationTreeChange, first, null);
- add((TreeChangeEventImpl)destinationTreeChange, CompositeElement.this, (TreeElement)firstChild);
- if(getTreeParent() != null){
- repairRemovedElement(CompositeElement.this, first);
- }
+ ChangeUtil.prepareAndRunChangeAction(destinationTreeChange -> {
+ TreeElement first = getFirstChildNode();
+ TreeChangeEventImpl event = (TreeChangeEventImpl)destinationTreeChange;
+ CompositeElement parent = getTreeParent();
+ if (parent != null) {
+ // treat all replacements as one big childrenChanged to simplify resulting PSI/document events
+ event.addElementaryChange(parent);
+ }
+ remove(event, first, null);
+ add(event, this, (TreeElement)firstChild);
+ if(parent != null) {
+ repairRemovedElement(this, first);
}
}, this);
}
diff --git a/xml/tests/src/com/intellij/codeInsight/XmlEventsTest.java b/xml/tests/src/com/intellij/codeInsight/XmlEventsTest.java
index 9b276996f6ca..c1033e51143c 100644
--- a/xml/tests/src/com/intellij/codeInsight/XmlEventsTest.java
+++ b/xml/tests/src/com/intellij/codeInsight/XmlEventsTest.java
@@ -15,11 +15,13 @@
*/
package com.intellij.codeInsight;
+import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.PomManager;
import com.intellij.pom.PomModel;
@@ -30,6 +32,7 @@ import com.intellij.pom.event.PomModelListener;
import com.intellij.pom.xml.XmlAspect;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiFileImpl;
+import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.*;
import com.intellij.testFramework.LightCodeInsightTestCase;
import com.intellij.util.DocumentUtil;
@@ -256,6 +259,22 @@ public class XmlEventsTest extends LightCodeInsightTestCase {
PsiManager.getInstance(getProject()).removePsiTreeChangeListener(listener);
}
+ public void testRangeMarkersShouldSurviveOnSmallTagValueModification() {
+ String text = "x y z a b c d";
+
+ PsiFile file = PsiFileFactory.getInstance(getProject()).createFileFromText("a.xml", XMLLanguage.INSTANCE, text);
+ Document document = file.getViewProvider().getDocument();
+ RangeMarker marker = document.createRangeMarker(TextRange.from(text.indexOf("a b"), 3));
+
+ XmlTag tag = PsiTreeUtil.findElementOfClassAtOffset(file, 0, XmlTag.class, false);
+ WriteCommandAction.runWriteCommandAction(null, () -> tag.getValue().setText("x a b c d"));
+
+ assertEquals("x a b c d", document.getText());
+ assertTrue(marker.isValid());
+ assertEquals("a b", document.getText(TextRange.create(marker)));
+ }
+
+
private static class TestListener extends PsiTreeChangeAdapter {
@Override
public void childReplaced(@NotNull PsiTreeChangeEvent event) {