From 8ec12188b2dd7ee0af67d70bb44aa86cfd03536b Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 26 Aug 2015 09:02:06 +0200 Subject: [PATCH] introduce smart pointer getPsiRange, let getLastCommittedDocument work for injections --- .../intellij/psi/IdentitySmartPointer.java | 6 ++ .../intellij/psi/SmartPsiElementPointer.java | 12 ++++ .../psi/impl/PsiDocumentManagerBase.java | 23 +++++++- .../impl/smartPointers/ClsElementInfo.java | 7 +++ .../impl/smartPointers/FileElementInfo.java | 13 +++++ .../impl/smartPointers/HardElementInfo.java | 5 ++ .../InjectedSelfElementInfo.java | 58 +++++++++++-------- .../impl/smartPointers/SelfElementInfo.java | 6 +- .../SmartPointerElementInfo.java | 3 + .../SmartPsiElementPointerImpl.java | 6 ++ .../psi/impl/PsiDocumentManagerImpl.java | 8 +++ .../tree/injected/MultiHostRegistrarImpl.java | 18 +++++- .../impl/source/tree/injected/ShredImpl.java | 15 +++-- 13 files changed, 146 insertions(+), 34 deletions(-) diff --git a/platform/core-api/src/com/intellij/psi/IdentitySmartPointer.java b/platform/core-api/src/com/intellij/psi/IdentitySmartPointer.java index 4c796b68d8a7..80c2a989ec1e 100644 --- a/platform/core-api/src/com/intellij/psi/IdentitySmartPointer.java +++ b/platform/core-api/src/com/intellij/psi/IdentitySmartPointer.java @@ -73,6 +73,12 @@ public class IdentitySmartPointer implements SmartPsiEleme @Override public Segment getRange() { + return getPsiRange(); + } + + @Nullable + @Override + public Segment getPsiRange() { return myElement.getTextRange(); } } diff --git a/platform/core-api/src/com/intellij/psi/SmartPsiElementPointer.java b/platform/core-api/src/com/intellij/psi/SmartPsiElementPointer.java index 11ca9f3f5b61..f2d8a1796422 100644 --- a/platform/core-api/src/com/intellij/psi/SmartPsiElementPointer.java +++ b/platform/core-api/src/com/intellij/psi/SmartPsiElementPointer.java @@ -45,6 +45,18 @@ public interface SmartPsiElementPointer { VirtualFile getVirtualFile(); + /** + * @return the range in the document. For committed document, it's the same as {@link #getPsiRange()}, for non-committed documents + * the ranges may be changed (like in {@link com.intellij.openapi.editor.RangeMarker}) or even invalidated. In the latter case returns null. + * Returns null for invalid pointers. + */ @Nullable Segment getRange(); + + /** + * @return the range in the committed PSI file. May be different from {@link #getRange()} result when the document has been changed since commit. + * Returns null for invalid pointers. + */ + @Nullable + Segment getPsiRange(); } diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java index 889b6e06b12b..4725c37155cf 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java @@ -61,6 +61,7 @@ import org.jetbrains.annotations.TestOnly; import javax.swing.*; import java.util.*; +import java.util.concurrent.ConcurrentMap; public abstract class PsiDocumentManagerBase extends PsiDocumentManager implements DocumentListener, ProjectComponent { static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiDocumentManagerImpl"); @@ -582,14 +583,31 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen } @NotNull - public FrozenDocument getLastCommittedDocument(@NotNull Document document) { - if (document instanceof FrozenDocument) return (FrozenDocument)document; + public DocumentEx getLastCommittedDocument(@NotNull Document document) { + if (document instanceof FrozenDocument) return (DocumentEx)document; + + if (document instanceof DocumentWindow) { + DocumentWindow window = (DocumentWindow)document; + Document delegate = window.getDelegate(); + if (delegate instanceof FrozenDocument) return (DocumentEx)window; + + UncommittedInfo info = myUncommittedInfos.get(delegate); + DocumentWindow answer = info == null ? null : info.myFrozenWindows.get(document); + if (answer == null) answer = freezeWindow(window); + if (info != null) answer = ConcurrencyUtil.cacheOrGet(info.myFrozenWindows, window, answer); + return (DocumentEx)answer; + } assert document instanceof DocumentImpl; UncommittedInfo info = myUncommittedInfos.get(document); return info != null ? info.myFrozen : ((DocumentImpl)document).freeze(); } + @NotNull + protected DocumentWindow freezeWindow(@NotNull DocumentWindow document) { + throw new UnsupportedOperationException(); + } + @NotNull public List getEventsSinceCommit(@NotNull Document document) { assert document instanceof DocumentImpl; @@ -899,6 +917,7 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen final DocumentImpl myOriginal; final FrozenDocument myFrozen; final List myEvents = ContainerUtil.newArrayList(); + final ConcurrentMap myFrozenWindows = ContainerUtil.newConcurrentMap(); public UncommittedInfo(DocumentImpl original) { myOriginal = original; diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/ClsElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/ClsElementInfo.java index 4d947cb83f3e..8c06af3277db 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/ClsElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/ClsElementInfo.java @@ -23,6 +23,7 @@ import com.intellij.psi.PsiAnchor; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * User: cdr @@ -68,6 +69,12 @@ public class ClsElementInfo extends SmartPointerElementInfo { return myStubIndexReference.getProject(); } + @Nullable + @Override + public Segment getPsiRange() { + return null; + } + @Override public PsiFile restoreFile() { return myStubIndexReference.getFile(); diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java index e7da9a06dc73..cd21f46a276e 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java @@ -16,14 +16,19 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.lang.Language; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Segment; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.impl.PsiDocumentManagerBase; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * User: cdr @@ -90,4 +95,12 @@ class FileElementInfo extends SmartPointerElementInfo { return myProject; } + @Nullable + @Override + public Segment getPsiRange() { + Document currentDoc = FileDocumentManager.getInstance().getCachedDocument(myVirtualFile); + Document committedDoc = currentDoc == null ? null : + ((PsiDocumentManagerBase)PsiDocumentManager.getInstance(myProject)).getLastCommittedDocument(currentDoc); + return committedDoc == null ? getRange() : new TextRange(0, committedDoc.getTextLength()); + } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java index 395375e750d4..910b016ac58f 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java @@ -68,6 +68,11 @@ class HardElementInfo extends SmartPointerElementInfo { return myElement.getTextRange(); } + @Override + public Segment getPsiRange() { + return getRange(); + } + @NotNull @Override public Project getProject() { diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java index 8c2091c71062..622032220097 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java @@ -28,6 +28,7 @@ import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.FreeThreadedFileViewProvider; +import com.intellij.psi.impl.PsiDocumentManagerBase; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -72,7 +73,13 @@ class InjectedSelfElementInfo extends SmartPointerElementInfo { @Override public Segment getRange() { - return getInjectedRange(); + return getInjectedRange(false); + } + + @Nullable + @Override + public Segment getPsiRange() { + return getInjectedRange(true); } @Override @@ -83,20 +90,14 @@ class InjectedSelfElementInfo extends SmartPointerElementInfo { PsiElement hostContext = myHostContext.getElement(); if (hostContext == null) return null; - Segment segment = myInjectedFileRangeInHostFile.getRange(); + Segment segment = myInjectedFileRangeInHostFile.getPsiRange(); if (segment == null) return null; - final TextRange rangeInHostFile = TextRange.create(segment); - PsiElement result = null; - PsiFile injectedPsi = getInjectedFileIn(hostContext, hostFile, rangeInHostFile); - if (injectedPsi != null) { - Document document = PsiDocumentManager.getInstance(getProject()).getDocument(injectedPsi); - int start = ((DocumentWindow)document).hostToInjected(rangeInHostFile.getStartOffset()); - int end = ((DocumentWindow)document).hostToInjected(rangeInHostFile.getEndOffset()); - result = SelfElementInfo.findElementInside(injectedPsi, start, end, anchorClass, anchorLanguage); - } + PsiFile injectedPsi = getInjectedFileIn(hostContext, hostFile, TextRange.create(segment)); + ProperTextRange rangeInInjected = hostToInjected(true, segment, injectedPsi); + if (rangeInInjected == null) return null; - return result; + return SelfElementInfo.findElementInside(injectedPsi, rangeInInjected.getStartOffset(), rangeInInjected.getEndOffset(), anchorClass, anchorLanguage); } private PsiFile getInjectedFileIn(@NotNull final PsiElement hostContext, @@ -161,24 +162,31 @@ class InjectedSelfElementInfo extends SmartPointerElementInfo { return getInjectedFileIn(hostContext, hostFile, rangeInHostFile); } - private ProperTextRange getInjectedRange() { - PsiFile hostFile = myHostContext.getContainingFile(); - if (hostFile == null || !hostFile.isValid()) return null; - + @Nullable + private ProperTextRange getInjectedRange(boolean psi) { PsiElement hostContext = myHostContext.getElement(); if (hostContext == null) return null; - Segment hostElementRange = myInjectedFileRangeInHostFile.getRange(); + Segment hostElementRange = psi ? myInjectedFileRangeInHostFile.getPsiRange() : myInjectedFileRangeInHostFile.getRange(); if (hostElementRange == null) return null; - PsiFile injectedFile = restoreFile(); - if (injectedFile == null) return null; - VirtualFile virtualFile = injectedFile.getVirtualFile(); - DocumentWindow documentWindow = virtualFile instanceof VirtualFileWindow ? ((VirtualFileWindow)virtualFile).getDocumentWindow() : null; - if (documentWindow==null) return null; - int start = documentWindow.hostToInjected(hostElementRange.getStartOffset()); - int end = documentWindow.hostToInjected(hostElementRange.getEndOffset()); - return ProperTextRange.create(start, end); + return hostToInjected(psi, hostElementRange, restoreFile()); + } + + @Nullable + private ProperTextRange hostToInjected(boolean psi, Segment hostRange, @Nullable PsiFile injectedFile) { + VirtualFile virtualFile = injectedFile == null ? null : injectedFile.getVirtualFile(); + if (virtualFile instanceof VirtualFileWindow) { + DocumentWindow documentWindow = ((VirtualFileWindow)virtualFile).getDocumentWindow(); + if (psi) { + documentWindow = (DocumentWindow) ((PsiDocumentManagerBase) PsiDocumentManager.getInstance(getProject())).getLastCommittedDocument(documentWindow); + } + int start = documentWindow.hostToInjected(hostRange.getStartOffset()); + int end = documentWindow.hostToInjected(hostRange.getEndOffset()); + return ProperTextRange.create(start, end); + } + + return null; } @Override diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java index 5a25f6db37f3..b4a686fe29bd 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java @@ -19,6 +19,7 @@ import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.editor.impl.FrozenDocument; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.*; @@ -109,7 +110,8 @@ public class SelfElementInfo extends SmartPointerElementInfo { } @Nullable - protected ProperTextRange getPsiRange() { + @Override + public ProperTextRange getPsiRange() { return calcPsiRange(); } @@ -248,7 +250,7 @@ public class SelfElementInfo extends SmartPointerElementInfo { PsiDocumentManagerBase documentManager = myManager.getPsiDocumentManager(); List events = documentManager.getEventsSinceCommit(document); if (!events.isEmpty()) { - return myMarkerCache.getUpdatedRange(markerCacheKey(), documentManager.getLastCommittedDocument(document), events); + return myMarkerCache.getUpdatedRange(markerCacheKey(), (FrozenDocument)documentManager.getLastCommittedDocument(document), events); } } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java index c975a59a959c..95a67782c5b5 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java @@ -51,4 +51,7 @@ abstract class SmartPointerElementInfo { public void cleanup() { } + + @Nullable + public abstract Segment getPsiRange(); } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java index 1b4002c074cf..34c74a318882 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java @@ -128,6 +128,12 @@ class SmartPsiElementPointerImpl implements SmartPointerEx return myElementInfo.getRange(); } + @Nullable + @Override + public Segment getPsiRange() { + return myElementInfo.getPsiRange(); + } + @NotNull private static SmartPointerElementInfo createElementInfo(@NotNull Project project, @NotNull E element, diff --git a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java index ce2a531fca3c..2fcc6ad63fcf 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java @@ -18,6 +18,7 @@ package com.intellij.psi.impl; import com.intellij.AppTopics; import com.intellij.injected.editor.DocumentWindow; +import com.intellij.injected.editor.DocumentWindowImpl; import com.intellij.injected.editor.EditorWindowImpl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.SettingsSavingComponent; @@ -39,6 +40,7 @@ import com.intellij.psi.FileViewProvider; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.impl.source.PostprocessReformattingAspect; +import com.intellij.psi.impl.source.tree.injected.MultiHostRegistrarImpl; import com.intellij.util.FileContentUtil; import com.intellij.util.Processor; import com.intellij.util.messages.MessageBus; @@ -188,4 +190,10 @@ public class PsiDocumentManagerImpl extends PsiDocumentManagerBase implements Se public void reparseFiles(@NotNull Collection files, boolean includeOpenFiles) { FileContentUtil.reparseFiles(myProject, files, includeOpenFiles); } + + @NotNull + @Override + protected DocumentWindow freezeWindow(@NotNull DocumentWindow document) { + return MultiHostRegistrarImpl.freezeWindow((DocumentWindowImpl)document); + } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java index 404ed93165b1..d3e9e8ec904f 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java @@ -55,7 +55,9 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtilCore; import com.intellij.testFramework.LightVirtualFile; +import com.intellij.util.Function; import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -189,7 +191,7 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT SmartPointerManagerImpl manager = (SmartPointerManagerImpl)SmartPointerManager.getInstance(myProject); shreds.add(new ShredImpl(manager.createSmartPsiFileRangePointer(myHostPsiFile, relevantRangeInHost, true), manager.createSmartPsiElementPointer(host, myHostPsiFile), - prefix, suffix, new ProperTextRange(startOffset, endOffset))); + prefix, suffix, new ProperTextRange(startOffset, endOffset), false)); return this; } @@ -551,4 +553,18 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT ReferenceInjector getReferenceInjector() { return myReferenceInjector; } + + @NotNull + public static DocumentWindow freezeWindow(@NotNull DocumentWindowImpl window) { + Place shreds = window.getShreds(); + Project project = shreds.getHostPointer().getProject(); + DocumentEx delegate = ((PsiDocumentManagerBase)PsiDocumentManager.getInstance(project)).getLastCommittedDocument(window.getDelegate()); + Place place = new Place(ContainerUtil.map(shreds, new Function() { + @Override + public PsiLanguageInjectionHost.Shred fun(final PsiLanguageInjectionHost.Shred shred) { + return ((ShredImpl) shred).withPsiRange(); + } + })); + return new DocumentWindowImpl(delegate, window.isOneLine(), place); + } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/ShredImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/ShredImpl.java index 56605f560cea..c5eb8158a412 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/ShredImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/ShredImpl.java @@ -28,20 +28,27 @@ class ShredImpl implements PsiLanguageInjectionHost.Shred { private final TextRange range; // range in (decoded) PSI private final String prefix; private final String suffix; + private final boolean usePsiRange; ShredImpl(@NotNull SmartPsiFileRange relevantRangeInHost, @NotNull SmartPsiElementPointer hostElementPointer, @NotNull String prefix, @NotNull String suffix, - @NotNull TextRange range) { + @NotNull TextRange range, + boolean usePsiRange) { this.hostElementPointer = hostElementPointer; this.relevantRangeInHost = relevantRangeInHost; this.prefix = prefix; this.suffix = suffix; this.range = range; + this.usePsiRange = usePsiRange; assert isValid(); } + ShredImpl withPsiRange() { + return new ShredImpl(relevantRangeInHost, hostElementPointer, prefix, suffix, range, true); + } + @NotNull public SmartPsiElementPointer getSmartPointer() { return hostElementPointer; @@ -50,18 +57,18 @@ class ShredImpl implements PsiLanguageInjectionHost.Shred { @Override @Nullable("returns null when the host document marker is invalid") public Segment getHostRangeMarker() { - return relevantRangeInHost.getRange(); + return usePsiRange ? relevantRangeInHost.getPsiRange() : relevantRangeInHost.getRange(); } @Override @NotNull public TextRange getRangeInsideHost() { PsiLanguageInjectionHost host = getHost(); - Segment psiRange = relevantRangeInHost.getRange(); + Segment psiRange = relevantRangeInHost.getPsiRange(); TextRange textRange = psiRange == null ? null : TextRange.create(psiRange); if (host == null) { if (textRange != null) return textRange; - Segment fromSP = hostElementPointer.getRange(); + Segment fromSP = hostElementPointer.getPsiRange(); if (fromSP != null) return TextRange.create(fromSP); return new TextRange(0,0); }