IDEA-175229 PSI changes should be reflected in the corresponding document even if PSI is non-physical

This commit is contained in:
peter
2017-07-07 08:00:21 +02:00
parent f3d06a9593
commit beade7a802
5 changed files with 34 additions and 11 deletions
@@ -345,7 +345,7 @@ public class PomModelImpl extends UserDataHolderBase implements PomModel {
}
boolean physical = changeScope.isPhysical();
if (physical && synchronizer.toProcessPsiEvent()) {
if (synchronizer.toProcessPsiEvent()) {
// fail-fast to prevent any psi modifications that would cause psi/document text mismatch
// PsiToDocumentSynchronizer assertions happen inside event processing and are logged by PsiManagerImpl.fireEvent instead of being rethrown
// so it's important to throw something outside event processing
@@ -353,7 +353,7 @@ public class PomModelImpl extends UserDataHolderBase implements PomModel {
throw new IllegalStateException("Attempt to modify PSI for non-committed Document!");
}
CommandProcessor commandProcessor = CommandProcessor.getInstance();
if (!commandProcessor.isUndoTransparentActionInProgress() && commandProcessor.getCurrentCommand() == null) {
if (physical && !commandProcessor.isUndoTransparentActionInProgress() && commandProcessor.getCurrentCommand() == null) {
throw new IncorrectOperationException("Must not change PSI outside command or undo-transparent action. See com.intellij.openapi.command.WriteCommandAction or com.intellij.openapi.command.CommandProcessor");
}
}
@@ -115,7 +115,7 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter {
return file.getUserData(PSI_DOCUMENT_ATOMIC_ACTION) == Boolean.TRUE;
}
static void performAtomically(@NotNull PsiFile file, @NotNull Runnable runnable) {
public static void performAtomically(@NotNull PsiFile file, @NotNull Runnable runnable) {
PsiUtilCore.ensureValid(file);
assert !isInsideAtomicChange(file);
file.putUserData(PSI_DOCUMENT_ATOMIC_ACTION, Boolean.TRUE);
@@ -370,7 +370,7 @@ public class DaemonListeners implements Disposable {
if (project != null && project != myProject) return false;
// cached is essential here since we do not want to create PSI file in alien project
PsiFile psiFile = myPsiDocumentManager.getCachedPsiFile(document);
return psiFile != null && psiFile.getOriginalFile() == psiFile;
return psiFile != null && psiFile.isPhysical() && psiFile.getOriginalFile() == psiFile;
}
@Override
@@ -16,7 +16,11 @@
package com.intellij.pom.wrappers;
import com.intellij.injected.editor.VirtualFileWindow;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.PomModel;
import com.intellij.pom.PomModelAspect;
import com.intellij.pom.event.PomModelEvent;
@@ -27,11 +31,12 @@ import com.intellij.pom.tree.events.TreeChange;
import com.intellij.pom.tree.events.TreeChangeEvent;
import com.intellij.pom.tree.events.impl.ChangeInfoImpl;
import com.intellij.pom.tree.events.impl.TreeChangeImpl;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.psi.impl.PsiTreeChangeEventImpl;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.impl.*;
import com.intellij.psi.impl.source.DummyHolder;
import com.intellij.testFramework.LightVirtualFile;
import java.util.Collections;
@@ -52,11 +57,12 @@ public class PsiEventWrapperAspect implements PomModelAspect{
private static void sendAfterEvents(TreeChangeEvent changeSet) {
ASTNode rootElement = changeSet.getRootElement();
final PsiFile file = (PsiFile)SourceTreeToPsiMap.treeElementToPsi(rootElement);
final PsiManagerImpl manager = (PsiManagerImpl)file.getManager();
PsiFile file = (PsiFile)rootElement.getPsi();
PsiManagerImpl manager = (PsiManagerImpl)file.getManager();
if(manager == null) return;
if (!file.isPhysical()) {
promoteNonPhysicalChangesToDocument(rootElement, file);
manager.afterChange(false);
return;
}
@@ -120,6 +126,20 @@ public class PsiEventWrapperAspect implements PomModelAspect{
}
}
private static void promoteNonPhysicalChangesToDocument(ASTNode rootElement, PsiFile file) {
if (file instanceof DummyHolder) return;
if (((PsiDocumentManagerImpl)PsiDocumentManager.getInstance(file.getProject())).isCommitInProgress()) return;
VirtualFile vFile = file.getViewProvider().getVirtualFile();
if (vFile instanceof LightVirtualFile && !(vFile instanceof VirtualFileWindow)) {
Document document = FileDocumentManager.getInstance().getCachedDocument(vFile);
if (document != null) {
CharSequence text = rootElement.getChars();
PsiToDocumentSynchronizer.performAtomically(file, () -> document.replaceString(0, document.getTextLength(), text));
}
}
}
private static boolean checkPsiForChildren(final ASTNode[] affectedChildren) {
for (final ASTNode astNode : affectedChildren) {
//if (TreeUtil.isCollapsedChameleon(astNode)) return false;
@@ -28,6 +28,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.SingleRootFileViewProvider;
import org.jetbrains.annotations.Nullable;
public class DocumentUndoProvider implements Disposable {
@@ -96,8 +97,10 @@ public class DocumentUndoProvider implements Disposable {
private boolean shouldRecordActions(final Document document) {
if (document.getUserData(UndoConstants.DONT_RECORD_UNDO) == Boolean.TRUE) return false;
final VirtualFile vFile = FileDocumentManager.getInstance().getFile(document);
return vFile == null || vFile.getUserData(UndoConstants.DONT_RECORD_UNDO) != Boolean.TRUE;
VirtualFile vFile = FileDocumentManager.getInstance().getFile(document);
if (vFile == null) return true;
return vFile.getUserData(SingleRootFileViewProvider.FREE_THREADED) != Boolean.TRUE &&
vFile.getUserData(UndoConstants.DONT_RECORD_UNDO) != Boolean.TRUE;
}
private void registerUndoableAction(DocumentEvent e) {