public void testNonAnchoredStubbedElement() { PsiFile file = configureByText(JavaFileType.INSTANCE, "class Foo { { @NotNull String foo; } }"); StubTree stubTree = ((PsiFileImpl)file).getStubTree(); assertNotNull(stubTree); PsiElement anno = stubTree.getPlainList().stream().map(StubElement::getPsi).filter(psiElement -> psiElement instanceof PsiAnnotation).findFirst().get(); SmartPsiElementPointer<PsiElement> pointer = createPointer(anno); assertNotNull(((PsiFileImpl)file).getStubTree()); stubTree = null; anno = null; PlatformTestUtil.tryGcSoftlyReachableObjects(); assertNull(((SmartPointerEx) pointer).getCachedElement()); file.getViewProvider().getDocument().insertString(0, " "); PsiDocumentManager.getInstance(myProject).commitAllDocuments(); assertNotNull(pointer.getElement()); }
@Override @Nullable public SmartPointerElementInfo createElementInfo(@NotNull PsiElement element, @NotNull PsiFile containingFile) { if (element instanceof StubBasedPsiElement && containingFile instanceof PsiFileWithStubSupport) { PsiFileWithStubSupport stubFile = (PsiFileWithStubSupport)containingFile; StubTree stubTree = stubFile.getStubTree(); if (stubTree != null) { // use stubs when tree is not loaded StubBasedPsiElement stubPsi = (StubBasedPsiElement)element; int stubId = PsiAnchor.calcStubIndex(stubPsi); IStubElementType myStubElementType = stubPsi.getElementType(); IElementType contentElementType = ((PsiFileImpl)containingFile).getContentElementType(); if (stubId != -1 && contentElementType instanceof IStubFileElementType) { // TemplateDataElementType is not IStubFileElementType return new AnchorElementInfo(element, stubFile, stubId, myStubElementType); } } } PsiElement anchor = getAnchor(element); if (anchor != null) { return new AnchorElementInfo(anchor, containingFile); } return null; }
public static int calcStubIndex(@NotNull StubBasedPsiElement psi) { if (psi instanceof PsiFile) { return 0; } final StubElement liveStub = psi.getStub(); if (liveStub != null) { return ((StubBase)liveStub).id; } PsiFileImpl file = (PsiFileImpl)psi.getContainingFile(); final StubTree stubTree = file.calcStubTree(); for (StubElement<?> stb : stubTree.getPlainList()) { if (stb.getPsi() == psi) { return ((StubBase)stb).id; } } return -1; // it is possible via custom stub builder intentionally not producing stubs for stubbed elements }
@Override @Nullable public SmartPointerElementInfo createElementInfo(@NotNull PsiElement element) { PsiFile containingFile = element.getContainingFile(); if (element instanceof StubBasedPsiElement && containingFile instanceof PsiFileWithStubSupport) { PsiFileWithStubSupport stubFile = (PsiFileWithStubSupport)containingFile; StubTree stubTree = stubFile.getStubTree(); if (stubTree != null) { // use stubs when tree is not loaded StubBasedPsiElement stubPsi = (StubBasedPsiElement)element; int stubId = PsiAnchor.calcStubIndex(stubPsi); IStubElementType myStubElementType = stubPsi.getElementType(); if (stubId != -1) { return new AnchorElementInfo(element, stubFile, stubId, myStubElementType); } } } PsiElement anchor = getAnchor(element); if (anchor != null) { return new AnchorElementInfo(anchor, containingFile); } return null; }
public static int calcStubIndex(StubBasedPsiElement psi) { if (psi instanceof PsiFile) { return 0; } final StubElement liveStub = psi.getStub(); if (liveStub != null) { return ((StubBase)liveStub).id; } PsiFileImpl file = (PsiFileImpl)psi.getContainingFile(); final StubTree stubTree = file.calcStubTree(); for (StubElement<?> stb : stubTree.getPlainList()) { if (stb.getPsi() == psi) { return ((StubBase)stb).id; } } return -1; // it is possible via custom stub builder intentionally not producing stubs for stubbed elements }
public static int calcStubIndex(@Nonnull StubBasedPsiElement psi) { if (psi instanceof PsiFile) { return 0; } final StubElement liveStub = psi.getStub(); if (liveStub != null) { return ((StubBase)liveStub).id; } PsiFileImpl file = (PsiFileImpl)psi.getContainingFile(); final StubTree stubTree = file.calcStubTree(); for (StubElement<?> stb : stubTree.getPlainList()) { if (stb.getPsi() == psi) { return ((StubBase)stb).id; } } return -1; // it is possible via custom stub builder intentionally not producing stubs for stubbed elements }
@Override public void onContentReload() { ApplicationManager.getApplication().assertWriteAccessAllowed(); synchronized(myStubLock) { StubTree stubTree = SoftReference.dereference(myStub); myStub = null; if(stubTree != null) { //noinspection unchecked ((PsiFileStubImpl) stubTree.getRoot()).clearPsi("cls onContentReload"); } } synchronized(myMirrorLock) { putUserData(CLS_DOCUMENT_LINK_KEY, null); myMirrorFileElement = null; myPackageStatement = null; } }
public static void bindStubsToTree(@NotNull PsiFileImpl file, @NotNull StubTree stubTree, @NotNull FileElement tree) throws StubBindingException { final Iterator<StubElement<?>> stubs = stubTree.getPlainList().iterator(); stubs.next(); // skip file root stub final IStubFileElementType type = file.getElementTypeForStubBuilder(); assert type != null; final StubBuilder builder = type.getBuilder(); tree.acceptTree(new RecursiveTreeElementWalkingVisitor() { @Override protected void visitNode(TreeElement node) { CompositeElement parent = node.getTreeParent(); if (parent != null && builder.skipChildProcessingWhenBuildingStubs(parent, node)) { return; } IElementType type = node.getElementType(); if (type instanceof IStubElementType && ((IStubElementType)type).shouldCreateStub(node)) { final StubElement stub = stubs.hasNext() ? stubs.next() : null; if (stub == null || stub.getStubType() != type) { throw new StubBindingException("stub:" + stub + ", AST:" + type); } //noinspection unchecked ((StubBase)stub).setPsi(node.getPsi()); } super.visitNode(node); } }); }
public static void bindStubsToTree(@Nonnull PsiFileImpl file, @Nonnull StubTree stubTree, @Nonnull FileElement tree) throws StubBindingException { final ListIterator<StubElement<?>> stubs = stubTree.getPlainList().listIterator(); stubs.next(); // skip file root stub final IStubFileElementType type = file.getElementTypeForStubBuilder(); assert type != null; final StubBuilder builder = type.getBuilder(); tree.acceptTree(new RecursiveTreeElementWalkingVisitor() { @Override protected void visitNode(TreeElement node) { CompositeElement parent = node.getTreeParent(); if (parent != null && builder.skipChildProcessingWhenBuildingStubs(parent, node)) { return; } IElementType type = node.getElementType(); if (type instanceof IStubElementType && ((IStubElementType)type).shouldCreateStub(node)) { final StubElement stub = stubs.hasNext() ? stubs.next() : null; if (stub == null || stub.getStubType() != type) { throw new StubBindingException("stub:" + stub + ", AST:" + type); } StubBasedPsiElementBase psi = (StubBasedPsiElementBase)node.getPsi(); //noinspection unchecked ((StubBase)stub).setPsi(psi); psi.setStubIndex(stubs.previousIndex()); } super.visitNode(node); } }); }
@Nullable @Override public Stub getStub(int stubIndex) { if (stubIndex < 0) return null; StubTree stubTree = getFileStubTree(); return stubTree == null ? null : stubTree.getPlainList().get(stubIndex); }
@Nullable @Override public Stub getGreenStub(int stubIndex) { if (stubIndex < 0) return null; StubTree stubTree = getContainingFile().getGreenStubTree(); return stubTree == null ? null : stubTree.getPlainList().get(stubIndex); }
FileTrees clearStub(@Nonnull String reason) { StubTree stubHolder = derefStub(); if (stubHolder != null) { ((PsiFileStubImpl<?>)stubHolder.getRoot()).clearPsi(reason); } return new FileTrees(null, myTreeElementPointer, astLoaded, useStrongRefs); }
@Nullable public static PsiElement restoreFromStubIndex(PsiFileWithStubSupport fileImpl, int index, @Nonnull IStubElementType elementType, boolean throwIfNull) { if (fileImpl == null) { if (throwIfNull) throw new AssertionError("Null file"); return null; } StubTree tree = fileImpl.getStubTree(); if (tree == null) { if (fileImpl instanceof PsiFileImpl) { // Note: as far as this is a realization of StubIndexReference fileImpl#getContentElementType() must be instance of IStubFileElementType tree = ((PsiFileImpl)fileImpl).calcStubTree(); } else { if (throwIfNull) throw new AssertionError("Not PsiFileImpl: " + fileImpl.getClass()); return null; } } List<StubElement<?>> list = tree.getPlainList(); if (index >= list.size()) { if (throwIfNull) throw new AssertionError("Too large index: " + index + ">=" + list.size()); return null; } StubElement stub = list.get(index); if (stub.getStubType() != elementType) { if (throwIfNull) throw new AssertionError("Element type mismatch: " + stub.getStubType() + "!=" + elementType); return null; } return stub.getPsi(); }
private static void assertTreeLoaded(PsiFileImpl file, boolean loaded) { FileElement treeElement = file.getTreeElement(); assertEquals(loaded, treeElement != null); StubTree stubTree = file.getStubTree(); assertEquals(loaded, stubTree == null); }
@Nullable StubTree getStubTree();
@Nullable ASTNode findTreeForStub(StubTree tree, StubElement<?> stub);
@Nullable public static PsiElement restoreFromStubIndex(PsiFileWithStubSupport fileImpl, int index, @NotNull IStubElementType elementType, boolean throwIfNull) { if (fileImpl == null) { if (throwIfNull) throw new AssertionError("Null file"); return null; } StubTree tree = fileImpl.getStubTree(); boolean foreign = tree == null; if (foreign) { if (fileImpl instanceof PsiFileImpl) { // Note: as far as this is a realization of StubIndexReference fileImpl#getContentElementType() must be instance of IStubFileElementType tree = ((PsiFileImpl)fileImpl).calcStubTree(); } else { if (throwIfNull) throw new AssertionError("Not PsiFileImpl: " + fileImpl.getClass()); return null; } } List<StubElement<?>> list = tree.getPlainList(); if (index >= list.size()) { if (throwIfNull) throw new AssertionError("Too large index: " + index + ">=" + list.size()); return null; } StubElement stub = list.get(index); if (stub.getStubType() != elementType) { if (throwIfNull) throw new AssertionError("Element type mismatch: " + stub.getStubType() + "!=" + elementType); return null; } if (foreign) { final PsiElement cachedPsi = ((StubBase)stub).getCachedPsi(); if (cachedPsi != null) return cachedPsi; final ASTNode ast = fileImpl.findTreeForStub(tree, stub); if (ast != null) { return ast.getPsi(); } if (throwIfNull) throw new AssertionError("No AST for stub"); return null; } return stub.getPsi(); }
@Nullable protected abstract StubTree getFileStubTree();
@Override protected StubTree getFileStubTree() { return myParent.getFileStubTree(); }
@Override protected StubTree getFileStubTree() { return SoftReference.dereference(myNode) == null ? myFile.getStubTree() : null; }
@Override protected StubTree getFileStubTree() { return myFile.getStubTree(); }
/** * Loads the AST for this file and returns the node which corresponds to the given stub */ @Nullable @RequiredReadAction ASTNode findTreeForStub(StubTree tree, StubElement<?> stub);
private FileTrees(@Nullable Reference<StubTree> stub, @Nullable Getter<FileElement> ast, boolean astLoaded, boolean useStrongRefs) { this.myStub = stub; this.myTreeElementPointer = ast; this.astLoaded = astLoaded; this.useStrongRefs = useStrongRefs; }
@Nullable StubTree derefStub() { return SoftReference.dereference(myStub); }
FileTrees withExclusiveStub(@Nonnull StubTree stub, Set<PsiFileImpl> allRoots) { if (derefTreeElement() != null || useStrongRefs) { throw new RuntimeException(toString() + "; roots=" + allRoots); } return new FileTrees(new SoftReference<>(stub), null, false, false); }
FileTrees withGreenStub(@Nonnull StubTree stub, @Nonnull PsiFileImpl file) { if (derefTreeElement() == null || !astLoaded) { throw new RuntimeException("No AST in file " + file + " of " + file.getClass() + "; " + this); } return new FileTrees(new SoftReference<>(stub), myTreeElementPointer, true, useStrongRefs); }
@Override public ASTNode findTreeForStub(final StubTree tree, final StubElement<?> stub) { return null; }
/** * @return the stub tree for this file, if it's stub-based at all. Will be null after the AST has been loaded * (e.g. by calling {@link PsiElement#getNode()} or {@link PsiElement#getText()}. */ @Nullable @RequiredReadAction StubTree getStubTree();