mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Transactions in psi to document synchronization
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user