diff --git a/pom/src/com/intellij/pom/PomModel.java b/pom/src/com/intellij/pom/PomModel.java index 53ea754de238..20db947b1b52 100644 --- a/pom/src/com/intellij/pom/PomModel.java +++ b/pom/src/com/intellij/pom/PomModel.java @@ -2,6 +2,7 @@ package com.intellij.pom; import com.intellij.openapi.util.UserDataHolder; import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.editor.Document; import com.intellij.pom.event.PomModelListener; import com.intellij.util.IncorrectOperationException; diff --git a/pom/src/com/intellij/pom/PomTransaction.java b/pom/src/com/intellij/pom/PomTransaction.java index 93fe1dfc9adb..b955162b4280 100644 --- a/pom/src/com/intellij/pom/PomTransaction.java +++ b/pom/src/com/intellij/pom/PomTransaction.java @@ -2,6 +2,7 @@ package com.intellij.pom; import com.intellij.pom.event.PomModelEvent; import com.intellij.util.IncorrectOperationException; +import com.intellij.psi.PsiElement; /** * Created by IntelliJ IDEA. @@ -12,4 +13,5 @@ import com.intellij.util.IncorrectOperationException; */ public interface PomTransaction{ PomModelEvent run() throws IncorrectOperationException; + PsiElement getChangeScope(); } diff --git a/pom/src/com/intellij/pom/impl/PomTransactionBase.java b/pom/src/com/intellij/pom/impl/PomTransactionBase.java new file mode 100644 index 000000000000..9bf0d5123016 --- /dev/null +++ b/pom/src/com/intellij/pom/impl/PomTransactionBase.java @@ -0,0 +1,15 @@ +package com.intellij.pom.impl; + +import com.intellij.pom.PomTransaction; +import com.intellij.psi.PsiElement; + +public abstract class PomTransactionBase implements PomTransaction{ + private PsiElement myScope; + public PomTransactionBase(PsiElement scope){ + myScope = scope; + } + + public PsiElement getChangeScope() { + return myScope; + } +} diff --git a/source/com/intellij/pom/core/impl/PomModelImpl.java b/source/com/intellij/pom/core/impl/PomModelImpl.java index 7360699570d6..ab2fd953d5a3 100644 --- a/source/com/intellij/pom/core/impl/PomModelImpl.java +++ b/source/com/intellij/pom/core/impl/PomModelImpl.java @@ -35,11 +35,15 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.editor.Document; import com.intellij.pom.*; import com.intellij.pom.event.PomModelEvent; import com.intellij.pom.event.PomModelListener; import com.intellij.util.IncorrectOperationException; import com.intellij.psi.PsiLock; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.impl.PsiDocumentManagerImpl; +import com.intellij.psi.impl.PsiToDocumentSynchronizer; import java.util.*; @@ -110,12 +114,20 @@ public class PomModelImpl extends UserDataHolderBase implements PomModel { private final Stack myBlockedAspects = new Stack(); - public synchronized void runTransaction(PomTransaction transaction, PomModelAspect aspect) throws IncorrectOperationException{ + public synchronized void runTransaction(PomTransaction transaction, + PomModelAspect aspect) throws IncorrectOperationException{ final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); if(progressIndicator != null) progressIndicator.startNonCancelableSection(); try{ synchronized(PsiLock.LOCK){ + final PsiDocumentManagerImpl manager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(myPomProject.getPsiProject()); + final PsiToDocumentSynchronizer synchronizer = manager.getSynchronizer(); + Document document = null; + if(transaction.getChangeScope().getContainingFile() != null) { + document = manager.getDocument(transaction.getChangeScope().getContainingFile()); + } myBlockedAspects.push(aspect); + if(document != null) synchronizer.startTransaction(document, transaction.getChangeScope()); final PomModelEvent event; try{ event = transaction.run(); @@ -125,6 +137,7 @@ public class PomModelImpl extends UserDataHolderBase implements PomModel { return; } finally{ + if(document != null) synchronizer.commitTransaction(document); myBlockedAspects.pop(); } diff --git a/source/com/intellij/pom/java/impl/PomJavaAspectImpl.java b/source/com/intellij/pom/java/impl/PomJavaAspectImpl.java index 321d3e83cb31..23387d4d2865 100644 --- a/source/com/intellij/pom/java/impl/PomJavaAspectImpl.java +++ b/source/com/intellij/pom/java/impl/PomJavaAspectImpl.java @@ -38,6 +38,7 @@ import com.intellij.pom.PomElement; import com.intellij.pom.PomModel; import com.intellij.pom.PomScope; import com.intellij.pom.PomTransaction; +import com.intellij.pom.impl.PomTransactionBase; import com.intellij.pom.event.PomModelEvent; import com.intellij.pom.java.LanguageLevel; import com.intellij.pom.java.PomJavaAspect; @@ -83,7 +84,7 @@ public class PomJavaAspectImpl extends PomJavaAspect implements ProjectComponent final PsiFile file1 = event.getOldParent().getContainingFile(); final PsiFile file2 = event.getNewParent().getContainingFile(); firePomEvent(file1); - if (!file1.equals(file2)) { + if (file1 != null && !file1.equals(file2)) { firePomEvent(file2); } } @@ -190,7 +191,7 @@ public class PomJavaAspectImpl extends PomJavaAspect implements ProjectComponent if (isJavaFile(file)) { final PomModel model = myProject.getModel(); try { - myProject.getModel().runTransaction(new PomTransaction() { + myProject.getModel().runTransaction(new PomTransactionBase(file) { public PomModelEvent run() { final PomModelEvent event = new PomModelEvent(model); final PomJavaAspectChangeSet set = new PomJavaAspectChangeSet(model, file); diff --git a/source/com/intellij/psi/impl/PsiDocumentManagerImpl.java b/source/com/intellij/psi/impl/PsiDocumentManagerImpl.java index 08f0a72d90ed..4c8f008a0855 100644 --- a/source/com/intellij/psi/impl/PsiDocumentManagerImpl.java +++ b/source/com/intellij/psi/impl/PsiDocumentManagerImpl.java @@ -21,6 +21,7 @@ import com.intellij.psi.*; import com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl; import com.intellij.psi.impl.source.PsiFileImpl; import com.intellij.psi.impl.source.text.BlockSupportImpl; +import com.intellij.psi.impl.source.tree.CompositeElement; import com.intellij.psi.text.BlockSupport; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.text.CharArrayUtil; @@ -48,6 +49,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec private SmartPointerManagerImpl mySmartPointerManager; private BlockSupportImpl myBlockSupport; private boolean myIsCommitInProgress; + private final PsiToDocumentSynchronizer mySynchronizer; private List myListeners = new ArrayList(); private Listener[] myCachedListeners = null; @@ -61,7 +63,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec myPsiManager = psiManager; mySmartPointerManager = (SmartPointerManagerImpl)smartPointerManager; myBlockSupport = (BlockSupportImpl)blockSupport; - myPsiManager.addPsiTreeChangeListener(new PsiToDocumentSynchronizer(this, mySmartPointerManager)); + myPsiManager.addPsiTreeChangeListener(mySynchronizer = new PsiToDocumentSynchronizer(this, mySmartPointerManager)); editorFactory.getEventMulticaster().addDocumentListener(this); } @@ -466,4 +468,8 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec if (myIsCommitInProgress) return true; return !myUncommittedDocuments.contains(doc); } + + public PsiToDocumentSynchronizer getSynchronizer() { + return mySynchronizer; + } } diff --git a/source/com/intellij/psi/impl/PsiToDocumentSynchronizer.java b/source/com/intellij/psi/impl/PsiToDocumentSynchronizer.java index b60cc8b6e07c..e3af041f5092 100644 --- a/source/com/intellij/psi/impl/PsiToDocumentSynchronizer.java +++ b/source/com/intellij/psi/impl/PsiToDocumentSynchronizer.java @@ -6,6 +6,8 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.ex.DocumentEx; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl; import com.intellij.psi.impl.source.SourceTreeToPsiMap; @@ -13,7 +15,9 @@ import com.intellij.psi.impl.source.jsp.JspxFileImpl; import com.intellij.psi.impl.source.tree.FileElement; import com.intellij.lang.ASTNode; -class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter { +import java.util.*; + +public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiToDocumentSynchronizer"); private final SmartPointerManagerImpl mySmartPointerManager; @@ -24,6 +28,10 @@ class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter { myPsiDocumentManager = psiDocumentManager; } + public DocumentChangeTransaction getTransaction(final Document document) { + return myTransactionsMap.get(document); + } + private static interface DocSyncAction { void syncDocument(Document document, PsiTreeChangeEventImpl event); } @@ -42,14 +50,18 @@ class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter { textBlock.clear(); } - ASTNode element = SourceTreeToPsiMap.psiElementToTree(event.getParent()); - while(element != null && !(element instanceof FileElement)) { - element = element.getTreeParent(); + boolean isOriginal = true; + if(event.getParent() != null){ + ASTNode element = SourceTreeToPsiMap.psiElementToTree(event.getParent()); + while(element != null && !(element instanceof FileElement)) { + element = element.getTreeParent(); + } + PsiFile fileForDoc = PsiDocumentManager.getInstance(psiFile.getProject()).getPsiFile(document); + + isOriginal = element != null ? fileForDoc == SourceTreeToPsiMap.treeElementToPsi(element) : false; + LOG.debug("DOCSync: " + isOriginal + "; document=" + document+"; file="+psiFile.getName() + ":" + + psiFile.getClass() +"; file for doc="+fileForDoc.getName()+"; virtualfile="+psiFile.getVirtualFile()); } - PsiFile fileForDoc = PsiDocumentManager.getInstance(psiFile.getProject()).getPsiFile(document); - boolean isOriginal = element != null ? fileForDoc == SourceTreeToPsiMap.treeElementToPsi(element) : false; - LOG.debug("DOCSync: " + isOriginal + "; document=" + document+"; file="+psiFile.getName() + ":" + - psiFile.getClass() +"; file for doc="+fileForDoc.getName()+"; virtualfile="+psiFile.getVirtualFile()); if (isOriginal) { myPsiDocumentManager.setProcessDocumentEvents(false); @@ -127,45 +139,66 @@ class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter { && application.getCurrentWriteAction(PsiExternalChangeAction.class) == null; } - private static void replaceString(Document document, int startOffset, int endOffset, String s) { - DocumentEx ex = (DocumentEx) document; - ex.suppressGuardedExceptions(); - try { - boolean isReadOnly = !document.isWritable(); - ex.setReadOnly(false); - ex.replaceString(startOffset, endOffset, s); - ex.setReadOnly(isReadOnly); + + private Map myTransactionsMap = new HashMap(); + + public void replaceString(Document document, int startOffset, int endOffset, String s) { + final DocumentChangeTransaction documentChangeTransaction = myTransactionsMap.get(document); + if(documentChangeTransaction != null) { + documentChangeTransaction.replace(startOffset, endOffset - startOffset, s); } - finally { - ex.unSuppressGuardedExceptions(); + else { + DocumentEx ex = (DocumentEx) document; + ex.suppressGuardedExceptions(); + try { + boolean isReadOnly = !document.isWritable(); + ex.setReadOnly(false); + ex.replaceString(startOffset, endOffset, s); + ex.setReadOnly(isReadOnly); + } + finally { + ex.unSuppressGuardedExceptions(); + } } } - private static void insertString(Document document, int offset, String s) { - DocumentEx ex = (DocumentEx) document; - ex.suppressGuardedExceptions(); - try { - boolean isReadOnly = !ex.isWritable(); - ex.setReadOnly(false); - ex.insertString(offset, s); - ex.setReadOnly(isReadOnly); + public void insertString(Document document, int offset, String s) { + final DocumentChangeTransaction documentChangeTransaction = myTransactionsMap.get(document); + if(documentChangeTransaction != null){ + documentChangeTransaction.replace(offset, 0, s); } - finally { - ex.unSuppressGuardedExceptions(); + else { + DocumentEx ex = (DocumentEx) document; + ex.suppressGuardedExceptions(); + try { + boolean isReadOnly = !ex.isWritable(); + ex.setReadOnly(false); + ex.insertString(offset, s); + ex.setReadOnly(isReadOnly); + } + finally { + ex.unSuppressGuardedExceptions(); + } } } - private static void deleteString(Document document, int startOffset, int endOffset){ - DocumentEx ex = (DocumentEx) document; - ex.suppressGuardedExceptions(); - try { - boolean isReadOnly = !ex.isWritable(); - ex.setReadOnly(false); - ex.deleteString(startOffset, endOffset); - ex.setReadOnly(isReadOnly); + public void deleteString(Document document, int startOffset, int endOffset){ + final DocumentChangeTransaction documentChangeTransaction = myTransactionsMap.get(document); + if(documentChangeTransaction != null){ + documentChangeTransaction.replace(startOffset, endOffset - startOffset, ""); } - finally { - ex.unSuppressGuardedExceptions(); + else { + DocumentEx ex = (DocumentEx) document; + ex.suppressGuardedExceptions(); + try { + boolean isReadOnly = !ex.isWritable(); + ex.setReadOnly(false); + ex.deleteString(startOffset, endOffset); + ex.setReadOnly(isReadOnly); + } + finally { + ex.unSuppressGuardedExceptions(); + } } } @@ -176,4 +209,236 @@ class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter { private TextBlock getTextBlock(Document document) { return myPsiDocumentManager.getTextBlock(document); } -} \ No newline at end of file + + public void startTransaction(Document doc, PsiElement scope) { + myTransactionsMap.put(doc, new DocumentChangeTransaction(doc, scope)); + } + + public void commitTransaction(Document document){ + final DocumentChangeTransaction documentChangeTransaction = myTransactionsMap.get(document); + if(documentChangeTransaction == null) return; + if(documentChangeTransaction.getTransactionRange() == null) return; // Nothing to do + try{ + final PsiElement changeScope = documentChangeTransaction.getChangeScope(); + final PsiTreeChangeEventImpl fakeEvent = new PsiTreeChangeEventImpl(changeScope.getManager()); + fakeEvent.setParent(changeScope); + fakeEvent.setFile(changeScope.getContainingFile()); + doSync(fakeEvent, new DocSyncAction() { + public void syncDocument(Document document, PsiTreeChangeEventImpl event) { + doCommitTransaction(document, documentChangeTransaction); + } + }); + } + finally{ + myTransactionsMap.remove(document); + } + } + + public void doCommitTransaction(final Document document){ + doCommitTransaction(document, myTransactionsMap.get(document)); + } + + private void doCommitTransaction(final Document document, + final DocumentChangeTransaction documentChangeTransaction) { + DocumentEx ex = (DocumentEx) document; + ex.suppressGuardedExceptions(); + try { + boolean isReadOnly = !document.isWritable(); + ex.setReadOnly(false); + final List> affectedFragments = documentChangeTransaction.getAffectedFragments(); + final Iterator> iterator = affectedFragments.iterator(); + while (iterator.hasNext()) { + final Pair pair = iterator.next(); + final StringBuffer replaceBuffer = pair.getSecond(); + final TextRange range = pair.getFirst(); + if(replaceBuffer.length() == 0){ + ex.deleteString(range.getStartOffset(), range.getEndOffset()); + } + else if(range.getLength() == 0){ + ex.insertString(range.getStartOffset(), replaceBuffer); + } + else{ + ex.replaceString(range.getStartOffset(), + range.getEndOffset(), + replaceBuffer); + } + } + ex.setReadOnly(isReadOnly); + } + finally { + ex.unSuppressGuardedExceptions(); + } + } + + public void cancelTransaction(Document doc) { + myTransactionsMap.remove(doc); + } + + public class DocumentChangeTransaction{ + List> myAffectedFragments = new ArrayList>(); + private TextRange myTransactionRange = null; + private final StringBuffer myTransactionBuffer = new StringBuffer(); + private final Document myDocument; + private final PsiElement myChangeScope; + + public DocumentChangeTransaction(final Document doc, PsiElement scope) { + myDocument = doc; + myChangeScope = scope; + } + + public TextRange getTransactionRange() { + return myTransactionRange; + } + + public List> getAffectedFragments() { + return myAffectedFragments; + } + + public PsiElement getChangeScope() { + return myChangeScope; + } + + public StringBuffer getTransactionBuffer() { + return myTransactionBuffer; + } + + public void replace(int start, int length, String str){ + final int startInFragment; + final StringBuffer fragmentReplaceText; + + { // calculating fragment + { // minimize replace + final int oldStart = start; + int end = start + length; + + final int newStringLength = str.length(); + final String chars = getText(start, length + start); + int newStartInString = 0; + int newEndInString = newStringLength; + { + while (newStartInString < newStringLength && + start < end && + str.charAt(newStartInString) == chars.charAt(start - oldStart)) { + start++; + newStartInString++; + } + + while (end > start && + newEndInString > newStartInString && + str.charAt(newEndInString - 1) == chars.charAt(end - oldStart - 1)) { + newEndInString--; + end--; + } + } + + str = str.substring(newStartInString, newEndInString); + length = end - start; + } + + final Pair fragment = getFragmentByRange(start, length); + fragmentReplaceText = fragment.getSecond(); + startInFragment = start - fragment.getFirst().getStartOffset(); + } + + fragmentReplaceText.replace(startInFragment, startInFragment + length, str); + } + + private String getText(final int start, final int end) { + int documentOffset = 0; + int effectiveOffset = 0; + StringBuffer text = new StringBuffer(); + Iterator> iterator = myAffectedFragments.iterator(); + while (iterator.hasNext() && effectiveOffset < end) { + final Pair pair = iterator.next(); + final TextRange range = pair.getFirst(); + final StringBuffer buffer = pair.getSecond(); + final int effectiveFragmentEnd = range.getStartOffset() + buffer.length(); + + if(range.getStartOffset() <= start && effectiveFragmentEnd >= end){ + return buffer.substring(start - range.getStartOffset(), end - range.getStartOffset()); + } + + if(range.getStartOffset() >= start){ + final int effectiveStart = Math.max(effectiveOffset, start); + text.append(myDocument.getChars(), + effectiveStart - effectiveOffset + documentOffset, + Math.min(range.getStartOffset(), end) - effectiveStart); + if(end > range.getStartOffset()){ + text.append(buffer.substring(0, Math.min(end - range.getStartOffset(), buffer.length()))); + } + } + + documentOffset += range.getEndOffset() - effectiveOffset; + effectiveOffset = range.getStartOffset() + buffer.length(); + } + + if(effectiveOffset < end){ + final int effectiveStart = Math.max(effectiveOffset, start); + text.append(myDocument.getChars(), + effectiveStart - effectiveOffset + documentOffset, + end - effectiveStart); + } + + return text.toString(); + } + + private Pair getFragmentByRange(int start, final int length) { + final StringBuffer fragmentBuffer = new StringBuffer(); + int end = start + length; + + { + // restoring buffer and remove all subfragments from the list + int documentOffset = 0; + int effectiveOffset = 0; + + Iterator> iterator = myAffectedFragments.iterator(); + while (iterator.hasNext() && effectiveOffset < end) { + final Pair pair = iterator.next(); + final TextRange range = pair.getFirst(); + final StringBuffer buffer = pair.getSecond(); + final int effectiveFragmentEnd = range.getStartOffset() + buffer.length(); + + if(range.getStartOffset() <= start && effectiveFragmentEnd >= end) return pair; + + if(effectiveFragmentEnd >= start){ + final int effectiveStart = Math.max(effectiveOffset, start); + if(range.getStartOffset() > start){ + fragmentBuffer.append(myDocument.getChars(), + effectiveStart - effectiveOffset + documentOffset, + Math.min(range.getStartOffset(), end) - effectiveStart); + } + if(end > range.getStartOffset()){ + fragmentBuffer.append(buffer); + end = end > effectiveFragmentEnd ? end - (buffer.length() - range.getLength()) : range.getEndOffset(); + start = Math.min(start, range.getStartOffset()); + iterator.remove(); + } + } + + documentOffset += range.getEndOffset() - effectiveOffset; + effectiveOffset = effectiveFragmentEnd; + } + + if(effectiveOffset < end){ + final int effectiveStart = Math.max(effectiveOffset, start); + fragmentBuffer.append(myDocument.getChars(), + effectiveStart - effectiveOffset + documentOffset, + end - effectiveStart); + } + + } + + final Pair pair = new Pair(new TextRange(start, end), fragmentBuffer); + int i; + for(i = 0; i < myAffectedFragments.size(); i++){ + if(start > pair.getFirst().getStartOffset()){ + myAffectedFragments.add(i, pair); + break; + } + } + if(i == myAffectedFragments.size()) myAffectedFragments.add(pair); + + return pair; + } + } +} diff --git a/source/com/intellij/psi/impl/source/xml/XmlAttributeImpl.java b/source/com/intellij/psi/impl/source/xml/XmlAttributeImpl.java index 056da01485c1..6ef6532b587a 100644 --- a/source/com/intellij/psi/impl/source/xml/XmlAttributeImpl.java +++ b/source/com/intellij/psi/impl/source/xml/XmlAttributeImpl.java @@ -18,6 +18,7 @@ import com.intellij.xml.XmlElementDescriptor; import com.intellij.xml.util.XmlUtil; import com.intellij.pom.PomModel; import com.intellij.pom.PomTransaction; +import com.intellij.pom.impl.PomTransactionBase; import com.intellij.pom.event.PomModelEvent; import com.intellij.lang.ASTNode; @@ -55,8 +56,8 @@ public class XmlAttributeImpl extends XmlElementImpl implements XmlAttribute { public void setValue(String valueText) throws IncorrectOperationException{ final ASTNode value = XmlChildRole.ATTRIBUTE_VALUE_FINDER.findChild(this); final PomModel model = getProject().getModel(); - final ASTNode newValue = XmlChildRole.ATTRIBUTE_VALUE_FINDER.findChild((ASTNode)getManager().getElementFactory().createXmlAttribute("a", valueText)); - model.runTransaction(new PomTransaction() { + final ASTNode newValue = XmlChildRole.ATTRIBUTE_VALUE_FINDER.findChild((CompositeElement)getManager().getElementFactory().createXmlAttribute("a", valueText)); + model.runTransaction(new PomTransactionBase(this) { public PomModelEvent run(){ if(value != null){ CodeEditUtil.replaceChild(XmlAttributeImpl.this, value, newValue); @@ -108,14 +109,14 @@ public class XmlAttributeImpl extends XmlElementImpl implements XmlAttribute { final ASTNode name = XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild(this); final String oldName = name.getText(); final PomModel model = getProject().getModel(); - final ASTNode newName = XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild((ASTNode)getManager().getElementFactory().createXmlAttribute(nameText, "")); - model.runTransaction(new PomTransaction() { + final ASTNode newName = XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild((CompositeElement)getManager().getElementFactory().createXmlAttribute(nameText, "")); + model.runTransaction(new PomTransactionBase(getParent()) { public PomModelEvent run(){ CodeEditUtil.replaceChild(XmlAttributeImpl.this, name, newName); return XmlAttributeSet.createXmlAttributeSet(model, getParent(), nameText, getValue()); } }, model.getModelAspect(XmlAspect.class)); - model.runTransaction(new PomTransaction() { + model.runTransaction(new PomTransactionBase(getParent()) { public PomModelEvent run(){ return XmlAttributeSet.createXmlAttributeSet(model, getParent(), oldName, null); } diff --git a/source/com/intellij/psi/impl/source/xml/XmlTagImpl.java b/source/com/intellij/psi/impl/source/xml/XmlTagImpl.java index 0dad2352ea06..720943baafda 100644 --- a/source/com/intellij/psi/impl/source/xml/XmlTagImpl.java +++ b/source/com/intellij/psi/impl/source/xml/XmlTagImpl.java @@ -9,6 +9,7 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.pom.PomModel; import com.intellij.pom.PomTransaction; +import com.intellij.pom.impl.PomTransactionBase; import com.intellij.pom.event.PomModelEvent; import com.intellij.psi.*; import com.intellij.psi.jsp.JspFile; @@ -355,15 +356,15 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag/*, Modification public PsiElement setName(final String name) throws IncorrectOperationException { final PomModel model = getProject().getModel(); - model.runTransaction(new PomTransaction() { + model.runTransaction(new PomTransactionBase(this) { public PomModelEvent run() throws IncorrectOperationException{ final String oldName = getName(); final XmlTagImpl dummyTag = (XmlTagImpl)getManager().getElementFactory().createTagFromText(XmlTagTextUtil.composeTagText(name, "aa")); final XmlTagImpl tag = XmlTagImpl.this; final CharTable charTableByTree = SharedImplUtil.findCharTableByTree(tag); - tag.replaceChild(XmlChildRole.START_TAG_NAME_FINDER.findChild(tag), ChangeUtil.copyElement((TreeElement)XmlChildRole.START_TAG_NAME_FINDER.findChild(dummyTag), charTableByTree)); + ChangeUtil.replaceChild(tag, (TreeElement)XmlChildRole.START_TAG_NAME_FINDER.findChild(tag), ChangeUtil.copyElement((TreeElement)XmlChildRole.START_TAG_NAME_FINDER.findChild(dummyTag), charTableByTree)); final ASTNode childByRole = XmlChildRole.CLOSING_TAG_NAME_FINDER.findChild(tag); - if(childByRole != null) tag.replaceChild(childByRole, ChangeUtil.copyElement((TreeElement)XmlChildRole.CLOSING_TAG_NAME_FINDER.findChild(dummyTag), charTableByTree)); + if(childByRole != null) ChangeUtil.replaceChild(tag, (TreeElement)childByRole, ChangeUtil.copyElement((TreeElement)XmlChildRole.CLOSING_TAG_NAME_FINDER.findChild(dummyTag), charTableByTree)); return XmlTagNameChanged.createXmlTagNameChanged(model, tag, oldName); } @@ -427,7 +428,7 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag/*, Modification myTags = result.toArray(new XmlTag[result.size()]); return myTags; } - + public XmlTag[] findSubTags(String name) { return findSubTags(name, null); } @@ -717,7 +718,7 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag/*, Modification final XmlAspect aspect = model.getModelAspect(XmlAspect.class); final TreeElement[] retHolder = new TreeElement[1]; if (child.getElementType() == XmlElementType.XML_ATTRIBUTE) { - model.runTransaction(new PomTransaction() { + model.runTransaction(new PomTransactionBase(this) { public PomModelEvent run(){ final String value = ((XmlAttribute)child).getValue(); final String name = ((XmlAttribute)child).getName(); @@ -750,7 +751,7 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag/*, Modification return transaction.getNewElement(); } else{ - model.runTransaction(new PomTransaction() { + model.runTransaction(new PomTransactionBase(this) { public PomModelEvent run() { final TreeElement treeElement = addInternalHack(child, child, anchor, Boolean.valueOf(before), fileType); retHolder[0] = treeElement; @@ -774,7 +775,7 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag/*, Modification if (treePrev.getElementType() == XmlElementType.XML_TEXT && treeNext.getElementType() == XmlElementType.XML_TEXT) { final XmlText xmlText = ((XmlText)SourceTreeToPsiMap.treeElementToPsi(treePrev)); xmlText.add(SourceTreeToPsiMap.treeElementToPsi(treeNext)); - model.runTransaction(new PomTransaction() { + model.runTransaction(new PomTransactionBase(this) { public PomModelEvent run() { final PomModelEvent event = new PomModelEvent(model); final XmlAspectChangeSet xmlAspectChangeSet = new XmlAspectChangeSet(model, (XmlFile)getContainingFile()); @@ -790,7 +791,7 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag/*, Modification // TODO[ik]: remove this hack if(fileType != StdFileTypes.XHTML){ - model.runTransaction(new PomTransaction() { + model.runTransaction(new PomTransactionBase(this) { public PomModelEvent run() throws IncorrectOperationException{ final Project project = getProject(); CodeStyleManager instance = CodeStyleManager.getInstance(project); @@ -808,7 +809,7 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag/*, Modification } } - model.runTransaction(new PomTransaction() { + model.runTransaction(new PomTransactionBase(this) { public PomModelEvent run() { if(child.getElementType() == XmlElementType.XML_ATTRIBUTE){ final String name = ((XmlAttribute)child).getName(); @@ -864,7 +865,7 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag/*, Modification return null; } - private class BodyInsertTransaction implements PomTransaction{ + private class BodyInsertTransaction extends PomTransactionBase{ private TreeElement myChild; private ASTNode myAnchor; private ASTNode myNewElement; @@ -873,6 +874,7 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag/*, Modification private FileType myFileType ; public BodyInsertTransaction(PomModel model, TreeElement child, ASTNode anchor, boolean beforeFlag, FileType fileType) { + super(XmlTagImpl.this); this.myModel = model; this.myChild = child; this.myAnchor = anchor; @@ -904,7 +906,7 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag/*, Modification return null; } } - + if (myAnchor == null) { ASTNode anchor = expandTag(); if(myChild.getElementType() == XmlElementType.XML_TAG){ diff --git a/source/com/intellij/psi/impl/source/xml/XmlTextImpl.java b/source/com/intellij/psi/impl/source/xml/XmlTextImpl.java index f3d7655a037e..8280321263ab 100644 --- a/source/com/intellij/psi/impl/source/xml/XmlTextImpl.java +++ b/source/com/intellij/psi/impl/source/xml/XmlTextImpl.java @@ -14,6 +14,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.xml.util.XmlTagTextUtil; import com.intellij.pom.PomModel; import com.intellij.pom.PomTransaction; +import com.intellij.pom.impl.PomTransactionBase; import com.intellij.pom.event.PomModelEvent; import com.intellij.lang.ASTNode; @@ -196,7 +197,7 @@ public class XmlTextImpl extends XmlElementImpl implements XmlText { final XmlText xmlText = getManager().getElementFactory().createTagFromText(" ").getValue().getTextElements()[0]; final ASTNode compositeElement = SourceTreeToPsiMap.psiElementToTree(xmlText); TreeUtil.removeRange((TreeElement)compositeElement.getFirstChildNode(), null); - model.runTransaction(new PomTransaction() { + model.runTransaction(new PomTransactionBase(this) { public PomModelEvent run() { ASTNode current = second; while (current != null) { @@ -218,7 +219,7 @@ public class XmlTextImpl extends XmlElementImpl implements XmlText { } } else { - model.runTransaction(new PomTransaction() { + model.runTransaction(new PomTransactionBase(this) { public PomModelEvent run() { retHolder[0] = addInternal((TreeElement)insertedElement, insertedElement, retHolder[0], Boolean.TRUE); return XmlTextChanged.createXmlTextChanged(model, XmlTextImpl.this, oldText); @@ -302,7 +303,7 @@ public class XmlTextImpl extends XmlElementImpl implements XmlText { if (start == end) return; try { - model.runTransaction(new PomTransaction() { + model.runTransaction(new PomTransactionBase(getParent()) { public PomModelEvent run() throws IncorrectOperationException { final String oldText = getText(); @@ -441,53 +442,53 @@ public class XmlTextImpl extends XmlElementImpl implements XmlText { if (child.getElementType() == XmlElementType.XML_TEXT) { if (child.getTextLength() == 0) return this; final XmlTextImpl text = (XmlTextImpl)child; - model.runTransaction(new PomTransaction() { - public PomModelEvent run() { - final String oldText = getText(); - ASTNode childBefore = (anchor != null ? (before ? anchor.getTreePrev() : anchor) : getLastChildNode()); - if (childBefore != null && childBefore.getElementType() == text.getFirstChildNode().getElementType()) { - final LeafElement newText = - mergeElements((LeafElement)childBefore, (LeafElement)text.getFirstChildNode(), - SharedImplUtil.findCharTableByTree(XmlTextImpl.this)); - if (newText != null) { - replaceChildInternal(childBefore, newText); - if (text.getLastChildNode() != text.getFirstChildNode()) { - addChildren(XmlTextImpl.this, (TreeElement)text.getFirstChildNode().getTreeNext(), null, anchor, - before); - } - } - else { - addChildren(XmlTextImpl.this, (TreeElement)text.getFirstChildNode(), null, anchor, before); - } - } - else { - ASTNode childAfter = (anchor != null ? (before ? anchor : anchor.getTreeNext()) : getLastChildNode()); - if (childAfter != null && childAfter.getElementType() == text.getFirstChildNode().getElementType()) { - final LeafElement newText = - mergeElements((LeafElement)text.getFirstChildNode(), (LeafElement)childAfter, - SharedImplUtil.findCharTableByTree(XmlTextImpl.this)); - if (newText != null) { - replaceChildInternal(childAfter, newText); - if (text.getLastChildNode() != text.getFirstChildNode()) { - addChildren(XmlTextImpl.this, (TreeElement)text.getFirstChildNode().getTreeNext(), null, anchor, - before); - } - } - else { - addChildren(XmlTextImpl.this, (TreeElement)text.getFirstChildNode(), null, anchor, before); - } - } + model.runTransaction(new PomTransactionBase(getParent()) { + public PomModelEvent run() { + final String oldText = getText(); + ASTNode childBefore = (anchor != null ? (before ? anchor.getTreePrev() : anchor) : getLastChildNode()); + if (childBefore != null && childBefore.getElementType() == text.getFirstChildNode().getElementType()) { + final LeafElement newText = + mergeElements((LeafElement)childBefore, (LeafElement)text.getFirstChildNode(), + SharedImplUtil.findCharTableByTree(XmlTextImpl.this)); + if (newText != null) { + replaceChildInternal(childBefore, newText); + if (text.getLastChildNode() != text.getFirstChildNode()) { + addChildren(XmlTextImpl.this, (TreeElement)text.getFirstChildNode().getTreeNext(), null, anchor, + before); + } + } + else { + addChildren(XmlTextImpl.this, (TreeElement)text.getFirstChildNode(), null, anchor, before); + } + } + else { + ASTNode childAfter = (anchor != null ? (before ? anchor : anchor.getTreeNext()) : getLastChildNode()); + if (childAfter != null && childAfter.getElementType() == text.getFirstChildNode().getElementType()) { + final LeafElement newText = + mergeElements((LeafElement)text.getFirstChildNode(), (LeafElement)childAfter, + SharedImplUtil.findCharTableByTree(XmlTextImpl.this)); + if (newText != null) { + replaceChildInternal(childAfter, newText); + if (text.getLastChildNode() != text.getFirstChildNode()) { + addChildren(XmlTextImpl.this, (TreeElement)text.getFirstChildNode().getTreeNext(), null, anchor, + before); + } + } + else { + addChildren(XmlTextImpl.this, (TreeElement)text.getFirstChildNode(), null, anchor, before); + } + } - addChildren(XmlTextImpl.this, (TreeElement)text.getFirstChildNode(), null, anchor, before); - } + addChildren(XmlTextImpl.this, (TreeElement)text.getFirstChildNode(), null, anchor, before); + } - retHolder[0] = XmlTextImpl.this; - return XmlTextChanged.createXmlTextChanged(model, XmlTextImpl.this, oldText); - } - }, aspect); + retHolder[0] = XmlTextImpl.this; + return XmlTextChanged.createXmlTextChanged(model, XmlTextImpl.this, oldText); + } + }, aspect); } else { - model.runTransaction(new PomTransaction() { + model.runTransaction(new PomTransactionBase(this) { public PomModelEvent run() { final String oldText = getText(); final TreeElement treeElement = addChildren(XmlTextImpl.this, child, child.getTreeNext(), anchor, before);