@Override @SuppressWarnings({"CloneDoesntDeclareCloneNotSupportedException", "CloneDoesntCallSuperClone"}) protected PsiFileImpl clone() { FileViewProvider viewProvider = getViewProvider(); FileViewProvider providerCopy = viewProvider.clone(); final Language language = getLanguage(); if (providerCopy == null) { throw new AssertionError( "Unable to clone the view provider: " + viewProvider + "; " + language); } PsiFileImpl clone = BlockSupportImpl.getFileCopy(this, providerCopy); copyCopyableDataTo(clone); if (getTreeElement() != null) { // not set by provider in clone final FileElement treeClone = (FileElement) calcTreeElement().clone(); clone.setTreeElementPointer( treeClone); // should not use setTreeElement here because cloned file still have // VirtualFile (SCR17963) treeClone.setPsi(clone); } if (viewProvider.isEventSystemEnabled()) { clone.myOriginalFile = this; } else if (myOriginalFile != null) { clone.myOriginalFile = myOriginalFile; } return clone; }
@Override protected PsiFile getPsiInner(@NotNull final Language target) { PsiFileImpl file = myRoots.get(target); if (file == null) { if (isPhysical()) { VirtualFile virtualFile = getVirtualFile(); if (isIgnored()) return null; VirtualFile parent = virtualFile.getParent(); if (parent != null) { getManager().findDirectory(parent); } } if (target != getBaseLanguage() && !getLanguages().contains(target)) { return null; } file = (PsiFileImpl) createFile(target); if (file == null) return null; if (myOriginal != null) { final PsiFile originalFile = myOriginal.getPsi(target); if (originalFile != null) { file.setOriginalFile(originalFile); } } file = ConcurrencyUtil.cacheOrGet(myRoots, target, file); } return file; }
@Override public void markInvalidated() { for (PsiFileImpl file : myRoots.values()) { file.markInvalidated(); } super.markInvalidated(); }
@NotNull public static PsiFileImpl getFileCopy( @NotNull PsiFileImpl originalFile, @NotNull FileViewProvider providerCopy) { FileViewProvider viewProvider = originalFile.getViewProvider(); Language language = originalFile.getLanguage(); PsiFile file = providerCopy.getPsi(language); if (file != null && !(file instanceof PsiFileImpl)) { throw new RuntimeException( "View provider " + viewProvider + " refused to provide PsiFileImpl for " + language + details(providerCopy, viewProvider)); } PsiFileImpl newFile = (PsiFileImpl) file; if (newFile == null && language == PlainTextLanguage.INSTANCE && originalFile == viewProvider.getPsi(viewProvider.getBaseLanguage())) { newFile = (PsiFileImpl) providerCopy.getPsi(providerCopy.getBaseLanguage()); } if (newFile == null) { throw new RuntimeException( "View provider " + viewProvider + " refused to parse text with " + language + details(providerCopy, viewProvider)); } return newFile; }
public static void doActualPsiChange(@NotNull final PsiFile file, final DiffLog diffLog) { file.getViewProvider().beforeContentsSynchronized(); try { final Document document = file.getViewProvider().getDocument(); PsiDocumentManagerImpl documentManager = (PsiDocumentManagerImpl) PsiDocumentManager.getInstance(file.getProject()); PsiToDocumentSynchronizer.DocumentChangeTransaction transaction = documentManager.getSynchronizer().getTransaction(document); final PsiFileImpl fileImpl = (PsiFileImpl) file; if (transaction == null) { final PomModel model = PomManager.getModel(fileImpl.getProject()); model.runTransaction( new PomTransactionBase(fileImpl, model.getModelAspect(TreeAspect.class)) { @Override public PomModelEvent runInner() { return new TreeAspectEvent(model, diffLog.performActualPsiChange(file)); } }); } else { diffLog.performActualPsiChange(file); } } catch (IncorrectOperationException e) { LOG.error(e); } }
@NotNull public static DiffLog mergeTrees( @NotNull final PsiFileImpl fileImpl, @NotNull final ASTNode oldRoot, @NotNull final ASTNode newRoot, @NotNull ProgressIndicator indicator) { if (newRoot instanceof FileElement) { ((FileElement) newRoot).setCharTable(fileImpl.getTreeElement().getCharTable()); } try { newRoot.putUserData(TREE_TO_BE_REPARSED, oldRoot); if (isReplaceWholeNode(fileImpl, newRoot)) { DiffLog treeChangeEvent = replaceElementWithEvents((CompositeElement) oldRoot, (CompositeElement) newRoot); fileImpl.putUserData(TREE_DEPTH_LIMIT_EXCEEDED, Boolean.TRUE); return treeChangeEvent; } newRoot .getFirstChildNode(); // maybe reparsed in PsiBuilderImpl and have thrown exception here } catch (ReparsedSuccessfullyException e) { // reparsed in PsiBuilderImpl return e.getDiffLog(); } finally { newRoot.putUserData(TREE_TO_BE_REPARSED, null); } final ASTShallowComparator comparator = new ASTShallowComparator(indicator); final ASTStructure treeStructure = createInterruptibleASTStructure(newRoot, indicator); DiffLog diffLog = new DiffLog(); diffTrees(oldRoot, diffLog, comparator, treeStructure, indicator); return diffLog; }
protected PsiFileImpl cloneImpl(FileElement treeElementClone) { PsiFileImpl clone = (PsiFileImpl) super.clone(); clone.setTreeElementPointer( treeElementClone); // should not use setTreeElement here because cloned file still have // VirtualFile (SCR17963) treeElementClone.setPsi(clone); return clone; }
@Override public void reparseRange(PsiFile file, int startOffset, int endOffset, CharSequence newTextS) throws IncorrectOperationException { LOG.assertTrue(file.isValid()); final PsiFileImpl psiFile = (PsiFileImpl) file; final Document document = psiFile.getViewProvider().getDocument(); assert document != null; document.replaceString(startOffset, endOffset, newTextS); PsiDocumentManager.getInstance(psiFile.getProject()).commitDocument(document); }
@NotNull private static DiffLog makeFullParse( ASTNode parent, @NotNull CharSequence newFileText, int textLength, @NotNull PsiFileImpl fileImpl, @NotNull ProgressIndicator indicator) { if (fileImpl instanceof PsiCodeFragment) { final FileElement holderElement = new DummyHolder(fileImpl.getManager(), null).getTreeElement(); holderElement.rawAddChildren( fileImpl.createContentLeafElement( holderElement.getCharTable().intern(newFileText, 0, textLength))); DiffLog diffLog = new DiffLog(); diffLog.appendReplaceFileElement( (FileElement) parent, (FileElement) holderElement.getFirstChildNode()); return diffLog; } else { FileViewProvider viewProvider = fileImpl.getViewProvider(); viewProvider.getLanguages(); FileType fileType = viewProvider.getVirtualFile().getFileType(); String fileName = fileImpl.getName(); final LightVirtualFile lightFile = new LightVirtualFile( fileName, fileType, newFileText, viewProvider.getVirtualFile().getCharset(), fileImpl.getViewProvider().getModificationStamp()); lightFile.setOriginalFile(viewProvider.getVirtualFile()); FileViewProvider copy = viewProvider.createCopy(lightFile); if (copy.isEventSystemEnabled()) { throw new AssertionError( "Copied view provider must be non-physical for reparse to deliver correct events: " + viewProvider); } copy.getLanguages(); SingleRootFileViewProvider.doNotCheckFileSizeLimit( lightFile); // optimization: do not convert file contents to bytes to determine if we // should codeinsight it PsiFileImpl newFile = getFileCopy(fileImpl, copy); newFile.setOriginalFile(fileImpl); final FileElement newFileElement = (FileElement) newFile.getNode(); final FileElement oldFileElement = (FileElement) fileImpl.getNode(); DiffLog diffLog = mergeTrees(fileImpl, oldFileElement, newFileElement, indicator); ((PsiManagerEx) fileImpl.getManager()).getFileManager().setViewProvider(lightFile, null); return diffLog; } }
@Nullable private PsiFileSystemItem doResolve(FileIncludeInfo info, PsiFile context) { PsiFileImpl psiFile = (PsiFileImpl) myPsiFileFactory.createFileFromText("dummy.txt", StdFileTypes.PLAIN_TEXT, info.path); psiFile.setOriginalFile(context); return new FileReferenceSet(psiFile) { @Override protected boolean useIncludingFileAsContext() { return false; } }.resolve(); }
@Override public void contentsSynchronized() { super.contentsSynchronized(); Set<Language> languages = getLanguages(); for (Iterator<Map.Entry<Language, PsiFileImpl>> iterator = myRoots.entrySet().iterator(); iterator.hasNext(); ) { Map.Entry<Language, PsiFileImpl> entry = iterator.next(); if (!languages.contains(entry.getKey())) { PsiFileImpl file = entry.getValue(); iterator.remove(); file.markInvalidated(); } } }
private static CompletionContext createCompletionContext( PsiFile hostCopy, int hostStartOffset, OffsetMap hostMap, PsiFile originalFile) { CompletionAssertions.assertHostInfo(hostCopy, hostMap); InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(hostCopy.getProject()); CompletionContext context; PsiFile injected = InjectedLanguageUtil.findInjectedPsiNoCommit(hostCopy, hostStartOffset); if (injected != null) { if (injected instanceof PsiFileImpl) { ((PsiFileImpl) injected).setOriginalFile(originalFile); } DocumentWindow documentWindow = InjectedLanguageUtil.getDocumentWindow(injected); CompletionAssertions.assertInjectedOffsets( hostStartOffset, injectedLanguageManager, injected, documentWindow); context = new CompletionContext(injected, translateOffsetMapToInjected(hostMap, documentWindow)); } else { context = new CompletionContext(hostCopy, hostMap); } CompletionAssertions.assertFinalOffsets(originalFile, context, injected); return context; }
@Override @NotNull public DiffLog reparseRange( @NotNull final PsiFile file, @NotNull TextRange changedPsiRange, @NotNull final CharSequence newFileText, @NotNull final ProgressIndicator indicator) { final PsiFileImpl fileImpl = (PsiFileImpl) file; final Couple<ASTNode> reparseableRoots = findReparseableRoots(fileImpl, changedPsiRange, newFileText); return reparseableRoots != null ? mergeTrees(fileImpl, reparseableRoots.first, reparseableRoots.second, indicator) : makeFullParse( fileImpl.getTreeElement(), newFileText, newFileText.length(), fileImpl, indicator); }
private ASTNode notBoundInExistingAst( PsiFileImpl file, FileElement treeElement, StubTree stubTree) { @NonNls String message = "this=" + this.getClass() + "; file.isPhysical=" + file.isPhysical() + "; node=" + myNode + "; file=" + file + "; tree=" + treeElement + "; stubTree=" + stubTree; PsiElement each = this; while (each != null) { message += "\n each of class " + each.getClass(); if (each instanceof StubBasedPsiElementBase) { message += "; node=" + ((StubBasedPsiElementBase) each).myNode + "; stub=" + ((StubBasedPsiElementBase) each).myStub; each = ((StubBasedPsiElementBase) each).getParentByStub(); } else { break; } } throw new AssertionError(message); }
@Override @Nullable protected PsiFile createFile(@NotNull final Language lang) { if (lang == getTemplateDataLanguage()) { final PsiFileImpl file = (PsiFileImpl) LanguageParserDefinitions.INSTANCE.forLanguage(StdLanguages.HTML).createFile(this); file.setContentElementType(EmbeddedPerlTokens.HTML_TEMPLATE_DATA); return file; } if (lang == getBaseLanguage()) { return LanguageParserDefinitions.INSTANCE.forLanguage(lang).createFile(this); } return null; }
@Override public long getModificationStamp() { if (!myFile.isContentsLoaded()) { unsetPsiContent(); return SingleRootFileViewProvider.this.getModificationStamp(); } return myModificationStamp; }
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 }
private static boolean isReplaceWholeNode(@NotNull PsiFileImpl fileImpl, @NotNull ASTNode newRoot) throws ReparsedSuccessfullyException { final Boolean data = fileImpl.getUserData(DO_NOT_REPARSE_INCREMENTALLY); if (data != null) fileImpl.putUserData(DO_NOT_REPARSE_INCREMENTALLY, null); boolean explicitlyMarkedDeep = Boolean.TRUE.equals(data); if (explicitlyMarkedDeep || isTooDeep(fileImpl)) { return true; } final ASTNode childNode = newRoot .getFirstChildNode(); // maybe reparsed in PsiBuilderImpl and have thrown exception here boolean childTooDeep = isTooDeep(childNode); if (childTooDeep) { childNode.putUserData(TREE_DEPTH_LIMIT_EXCEEDED, null); fileImpl.putUserData(TREE_DEPTH_LIMIT_EXCEEDED, Boolean.TRUE); } return childTooDeep; }
@Override @NotNull public ASTNode getNode() { ASTNode node = myNode; if (node == null) { ApplicationManager.getApplication().assertReadAccessAllowed(); PsiFileImpl file = (PsiFileImpl) getContainingFile(); synchronized (file.getStubLock()) { node = myNode; if (node == null) { NonCancelableSection criticalSection = ProgressIndicatorProvider.startNonCancelableSectionIfSupported(); try { if (!file.isValid()) throw new PsiInvalidElementAccessException(this); FileElement treeElement = file.getTreeElement(); StubTree stubTree = file.getStubTree(); if (treeElement != null) { return notBoundInExistingAst(file, treeElement, stubTree); } final FileElement fileElement = file.loadTreeElement(); node = myNode; if (node == null) { @NonNls String message = "Failed to bind stub to AST for element " + getClass() + " in " + (file.getVirtualFile() == null ? "<unknown file>" : file.getVirtualFile().getPath()) + "\nFile stub tree:\n" + (stubTree != null ? StringUtil.trimLog( ((PsiFileStubImpl) stubTree.getRoot()).printTree(), 1024) : " is null") + "\nLoaded file AST:\n" + StringUtil.trimLog(DebugUtil.treeToString(fileElement, true), 1024); throw new IllegalArgumentException(message); } } finally { criticalSection.done(); } } } } return node; }
@Override public CharSequence getText() { if (!myFile.isContentsLoaded()) { unsetPsiContent(); return getContents(); } if (myContent != null) return myContent; return myContent = ApplicationManager.getApplication() .runReadAction( new Computable<CharSequence>() { public CharSequence compute() { return myFile.calcTreeElement().getText(); } }); }
@Override public PsiElement getMirror() { TreeElement mirrorTreeElement = myMirrorFileElement; if (mirrorTreeElement == null) { synchronized (myMirrorLock) { mirrorTreeElement = myMirrorFileElement; if (mirrorTreeElement == null) { VirtualFile file = getVirtualFile(); PsiClass[] classes = getClasses(); String fileName = (classes.length > 0 ? classes[0].getName() : file.getNameWithoutExtension()) + JavaFileType.DOT_DEFAULT_EXTENSION; final Document document = FileDocumentManager.getInstance().getDocument(file); assert document != null : file.getUrl(); CharSequence mirrorText = document.getImmutableCharSequence(); boolean internalDecompiler = StringUtil.startsWith(mirrorText, BANNER); PsiFileFactory factory = PsiFileFactory.getInstance(getManager().getProject()); PsiFile mirror = factory.createFileFromText(fileName, JavaLanguage.INSTANCE, mirrorText, false, false); mirror.putUserData(PsiUtil.FILE_LANGUAGE_LEVEL_KEY, getLanguageLevel()); mirrorTreeElement = SourceTreeToPsiMap.psiToTreeNotNull(mirror); try { final TreeElement finalMirrorTreeElement = mirrorTreeElement; ProgressManager.getInstance() .executeNonCancelableSection( new Runnable() { @Override public void run() { setMirror(finalMirrorTreeElement); putUserData(CLS_DOCUMENT_LINK_KEY, document); } }); } catch (InvalidMirrorException e) { //noinspection ThrowableResultOfMethodCallIgnored LOG.error(file.getUrl(), internalDecompiler ? e : wrapException(e, file)); } ((PsiFileImpl) mirror).setOriginalFile(this); myMirrorFileElement = mirrorTreeElement; } } } return mirrorTreeElement.getPsi(); }
protected PsiFile getPsiInner(final Language target) { PsiFile file = myRoots.get(target); if (file == null) { if (isPhysical()) { VirtualFile virtualFile = getVirtualFile(); VirtualFile parent = virtualFile.getParent(); if (parent != null) { getManager().findDirectory(parent); } } file = createFile(target); if (file == null) return null; if (myOriginal != null) { final PsiFile originalFile = myOriginal.getPsi(target); if (originalFile != null) { ((PsiFileImpl) file).setOriginalFile(originalFile); } } file = ConcurrencyUtil.cacheOrGet(myRoots, target, file); } return file; }
@Override public void putInfo(@NotNull Map<String, String> info) { PsiFileImpl.putInfo(this, info); }
protected void removeFile(final Language language) { PsiFileImpl file = myRoots.remove(language); if (file != null) { file.markInvalidated(); } }
public void beforeDocumentChanged() { final PsiFileImpl psiFile = (PsiFileImpl) getCachedPsi(getBaseLanguage()); if (psiFile != null && psiFile.isContentsLoaded() && getContent() instanceof DocumentContent) { setContent(new PsiFileContent(psiFile, getModificationStamp())); } }
/** * This method searches ast node that could be reparsed incrementally and returns pair of target * reparseable node and new replacement node. Returns null if there is no any chance to make * incremental parsing. */ @Nullable public Couple<ASTNode> findReparseableRoots( @NotNull PsiFileImpl file, @NotNull TextRange changedPsiRange, @NotNull CharSequence newFileText) { Project project = file.getProject(); final FileElement fileElement = file.getTreeElement(); final CharTable charTable = fileElement.getCharTable(); int lengthShift = newFileText.length() - fileElement.getTextLength(); if (fileElement.getElementType() instanceof ITemplateDataElementType || isTooDeep(file)) { // unable to perform incremental reparse for template data in JSP, or in exceptionally deep // trees return null; } final ASTNode leafAtStart = fileElement.findLeafElementAt(Math.max(0, changedPsiRange.getStartOffset() - 1)); final ASTNode leafAtEnd = fileElement.findLeafElementAt( Math.min(changedPsiRange.getEndOffset(), fileElement.getTextLength() - 1)); ASTNode node = leafAtStart != null && leafAtEnd != null ? TreeUtil.findCommonParent(leafAtStart, leafAtEnd) : fileElement; Language baseLanguage = file.getViewProvider().getBaseLanguage(); while (node != null && !(node instanceof FileElement)) { IElementType elementType = node.getElementType(); if (elementType instanceof IReparseableElementType) { final TextRange textRange = node.getTextRange(); final IReparseableElementType reparseable = (IReparseableElementType) elementType; if (baseLanguage.isKindOf(reparseable.getLanguage()) && textRange.getLength() + lengthShift > 0) { final int start = textRange.getStartOffset(); final int end = start + textRange.getLength() + lengthShift; if (end > newFileText.length()) { reportInconsistentLength(file, newFileText, node, start, end); break; } CharSequence newTextStr = newFileText.subSequence(start, end); if (reparseable.isParsable(node.getTreeParent(), newTextStr, baseLanguage, project)) { ASTNode chameleon = reparseable.createNode(newTextStr); if (chameleon != null) { DummyHolder holder = DummyHolderFactory.createHolder( file.getManager(), null, node.getPsi(), charTable); holder.getTreeElement().rawAddChildren((TreeElement) chameleon); if (holder.getTextLength() != newTextStr.length()) { String details = ApplicationManager.getApplication().isInternal() ? "text=" + newTextStr + "; treeText=" + holder.getText() + ";" : ""; LOG.error("Inconsistent reparse: " + details + " type=" + elementType); } return Couple.of(node, chameleon); } } } } node = node.getTreeParent(); } return null; }