From beade7a802d3da44cbd1cfcb0dfbdbbc41ee450f Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 7 Jul 2017 07:51:56 +0200 Subject: [PATCH] IDEA-175229 PSI changes should be reflected in the corresponding document even if PSI is non-physical --- .../intellij/pom/core/impl/PomModelImpl.java | 4 +-- .../psi/impl/PsiToDocumentSynchronizer.java | 2 +- .../daemon/impl/DaemonListeners.java | 2 +- .../pom/wrappers/PsiEventWrapperAspect.java | 30 +++++++++++++++---- .../command/impl/DocumentUndoProvider.java | 7 +++-- 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/platform/core-impl/src/com/intellij/pom/core/impl/PomModelImpl.java b/platform/core-impl/src/com/intellij/pom/core/impl/PomModelImpl.java index 4d935b2fe83f..a6b0852fca1e 100644 --- a/platform/core-impl/src/com/intellij/pom/core/impl/PomModelImpl.java +++ b/platform/core-impl/src/com/intellij/pom/core/impl/PomModelImpl.java @@ -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"); } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiToDocumentSynchronizer.java b/platform/core-impl/src/com/intellij/psi/impl/PsiToDocumentSynchronizer.java index e74065d10838..5cba3ac0a3d5 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiToDocumentSynchronizer.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiToDocumentSynchronizer.java @@ -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); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java index fe07f250f5e7..456cf35f3ec1 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java @@ -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 diff --git a/platform/lang-impl/src/com/intellij/pom/wrappers/PsiEventWrapperAspect.java b/platform/lang-impl/src/com/intellij/pom/wrappers/PsiEventWrapperAspect.java index 54c461caeed4..c2beb0bb548b 100644 --- a/platform/lang-impl/src/com/intellij/pom/wrappers/PsiEventWrapperAspect.java +++ b/platform/lang-impl/src/com/intellij/pom/wrappers/PsiEventWrapperAspect.java @@ -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; diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentUndoProvider.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentUndoProvider.java index 1a5025fb356d..9d713bf56482 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentUndoProvider.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentUndoProvider.java @@ -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) {