Transactions in psi to document synchronization

This commit is contained in:
no_reply@jetbrains.com
2005-01-22 01:39:55 +03:00
parent df752b5d8a
commit de3f768d94
10 changed files with 412 additions and 105 deletions
+1
View File
@@ -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;
@@ -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();
}
@@ -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;
}
}
@@ -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<PomModelAspect> myBlockedAspects = new Stack<PomModelAspect>();
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();
}
@@ -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);
@@ -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<Listener> myListeners = new ArrayList<Listener>();
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;
}
}
@@ -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<Document, DocumentChangeTransaction> myTransactionsMap = new HashMap<Document, DocumentChangeTransaction>();
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);
}
}
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<Pair<TextRange, StringBuffer>> affectedFragments = documentChangeTransaction.getAffectedFragments();
final Iterator<Pair<TextRange, StringBuffer>> iterator = affectedFragments.iterator();
while (iterator.hasNext()) {
final Pair<TextRange, StringBuffer> 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<Pair<TextRange,StringBuffer>> myAffectedFragments = new ArrayList<Pair<TextRange, StringBuffer>>();
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<Pair<TextRange, StringBuffer>> 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<TextRange, StringBuffer> 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<Pair<TextRange, StringBuffer>> iterator = myAffectedFragments.iterator();
while (iterator.hasNext() && effectiveOffset < end) {
final Pair<TextRange, StringBuffer> 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<TextRange, StringBuffer> 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<Pair<TextRange, StringBuffer>> iterator = myAffectedFragments.iterator();
while (iterator.hasNext() && effectiveOffset < end) {
final Pair<TextRange, StringBuffer> 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<TextRange, StringBuffer> pair = new Pair<TextRange, StringBuffer>(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;
}
}
}
@@ -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);
}
@@ -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){
@@ -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("<tag> </tag>").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);