introduce smart pointer getPsiRange, let getLastCommittedDocument work for injections

This commit is contained in:
peter
2015-08-26 10:06:50 +02:00
parent f116b27261
commit 8ec12188b2
13 changed files with 146 additions and 34 deletions
@@ -73,6 +73,12 @@ public class IdentitySmartPointer<T extends PsiElement> implements SmartPsiEleme
@Override
public Segment getRange() {
return getPsiRange();
}
@Nullable
@Override
public Segment getPsiRange() {
return myElement.getTextRange();
}
}
@@ -45,6 +45,18 @@ public interface SmartPsiElementPointer<E extends PsiElement> {
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();
}
@@ -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<DocumentEvent> 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<DocumentEvent> myEvents = ContainerUtil.newArrayList();
final ConcurrentMap<DocumentWindow, DocumentWindow> myFrozenWindows = ContainerUtil.newConcurrentMap();
public UncommittedInfo(DocumentImpl original) {
myOriginal = original;
@@ -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();
@@ -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());
}
}
@@ -68,6 +68,11 @@ class HardElementInfo extends SmartPointerElementInfo {
return myElement.getTextRange();
}
@Override
public Segment getPsiRange() {
return getRange();
}
@NotNull
@Override
public Project getProject() {
@@ -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
@@ -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<DocumentEvent> events = documentManager.getEventsSinceCommit(document);
if (!events.isEmpty()) {
return myMarkerCache.getUpdatedRange(markerCacheKey(), documentManager.getLastCommittedDocument(document), events);
return myMarkerCache.getUpdatedRange(markerCacheKey(), (FrozenDocument)documentManager.getLastCommittedDocument(document), events);
}
}
}
@@ -51,4 +51,7 @@ abstract class SmartPointerElementInfo {
public void cleanup() {
}
@Nullable
public abstract Segment getPsiRange();
}
@@ -128,6 +128,12 @@ class SmartPsiElementPointerImpl<E extends PsiElement> implements SmartPointerEx
return myElementInfo.getRange();
}
@Nullable
@Override
public Segment getPsiRange() {
return myElementInfo.getPsiRange();
}
@NotNull
private static <E extends PsiElement> SmartPointerElementInfo createElementInfo(@NotNull Project project,
@NotNull E element,
@@ -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<VirtualFile> files, boolean includeOpenFiles) {
FileContentUtil.reparseFiles(myProject, files, includeOpenFiles);
}
@NotNull
@Override
protected DocumentWindow freezeWindow(@NotNull DocumentWindow document) {
return MultiHostRegistrarImpl.freezeWindow((DocumentWindowImpl)document);
}
}
@@ -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<PsiLanguageInjectionHost.Shred, PsiLanguageInjectionHost.Shred>() {
@Override
public PsiLanguageInjectionHost.Shred fun(final PsiLanguageInjectionHost.Shred shred) {
return ((ShredImpl) shred).withPsiRange();
}
}));
return new DocumentWindowImpl(delegate, window.isOneLine(), place);
}
}
@@ -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<PsiLanguageInjectionHost> 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<PsiLanguageInjectionHost> 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);
}