private void reparseFile(final PsiFile file, FileElement treeElement, CharSequence newText) { PsiToDocumentSynchronizer synchronizer =((PsiDocumentManagerBase)PsiDocumentManager.getInstance(myProject)).getSynchronizer(); TextRange changedPsiRange = DocumentCommitProcessor.getChangedPsiRange(file, treeElement, newText); if (changedPsiRange == null) return; final DiffLog log = BlockSupport.getInstance(myProject).reparseRange(file, changedPsiRange, newText, new EmptyProgressIndicator(), treeElement.getText()); synchronizer.setIgnorePsiEvents(true); try { CodeStyleManager.getInstance(file.getProject()).performActionWithFormatterDisabled(new Runnable() { @Override public void run() { runTransaction(new PomTransactionBase(file, getModelAspect(TreeAspect.class)) { @Nullable @Override public PomModelEvent runInner() throws IncorrectOperationException { return new TreeAspectEvent(PomModelImpl.this, log.performActualPsiChange(file)); } }); } }); } finally { synchronizer.setIgnorePsiEvents(false); } }
@NotNull private ASTNode buildTree() { final StartMarker rootMarker = prepareLightTree(); final boolean isTooDeep = myFile != null && BlockSupport.isTooDeep(myFile.getOriginalFile()); if (myOriginalTree != null && !isTooDeep) { DiffLog diffLog = merge(myOriginalTree, rootMarker, myLastCommittedText); throw new BlockSupport.ReparsedSuccessfullyException(diffLog); } final ASTNode rootNode = createRootAST(rootMarker); bind(rootMarker, (CompositeElement)rootNode); if (isTooDeep && !(rootNode instanceof FileElement)) { final ASTNode childNode = rootNode.getFirstChildNode(); childNode.putUserData(BlockSupport.TREE_DEPTH_LIMIT_EXCEEDED, Boolean.TRUE); } return rootNode; }
private ASTNode buildTree() { final StartMarker rootMarker = prepareLightTree(); final boolean isTooDeep = myFile != null && BlockSupport.isTooDeep(myFile.getOriginalFile()); if (myOriginalTree != null && !isTooDeep) { DiffLog diffLog = merge(myOriginalTree, rootMarker); throw new BlockSupport.ReparsedSuccessfullyException(diffLog); } final ASTNode rootNode = createRootAST(rootMarker); bind(rootMarker, (CompositeElement)rootNode); if (isTooDeep && !(rootNode instanceof FileElement)) { final ASTNode childNode = rootNode.getFirstChildNode(); childNode.putUserData(BlockSupport.TREE_DEPTH_LIMIT_EXCEEDED, Boolean.TRUE); } return rootNode; }
@Nullable private Runnable reparseFile(@Nonnull final PsiFile file, @Nonnull FileElement treeElement, @Nonnull CharSequence newText) { TextRange changedPsiRange = ChangedPsiRangeUtil.getChangedPsiRange(file, treeElement, newText); if (changedPsiRange == null) return null; Runnable reparseLeaf = tryReparseOneLeaf(treeElement, newText, changedPsiRange); if (reparseLeaf != null) return reparseLeaf; final DiffLog log = BlockSupport.getInstance(myProject).reparseRange(file, treeElement, changedPsiRange, newText, new EmptyProgressIndicator(), treeElement.getText()); return () -> runTransaction(new PomTransactionBase(file, getModelAspect(TreeAspect.class)) { @Override public PomModelEvent runInner() throws IncorrectOperationException { return new TreeAspectEvent(PomModelImpl.this, log.performActualPsiChange(file)); } }); }
@SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") public void reparseFileFromText(@Nonnull PsiFileImpl file) { ApplicationManager.getApplication().assertIsDispatchThread(); if (isCommitInProgress()) throw new IllegalStateException("Re-entrant commit is not allowed"); FileElement node = file.calcTreeElement(); CharSequence text = node.getChars(); ourIsFullReparseInProgress = true; try { WriteAction.run(() -> { ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator(); if (indicator == null) indicator = new EmptyProgressIndicator(); DiffLog log = BlockSupportImpl.makeFullParse(file, node, text, indicator, text).getFirst(); log.doActualPsiChange(file); file.getViewProvider().contentsSynchronized(); }); } finally { ourIsFullReparseInProgress = false; } }
public static void ensureCorrectReparse(@NotNull final PsiFile file) { final String psiToStringDefault = DebugUtil.psiToString(file, false, false); final String fileText = file.getText(); final DiffLog diffLog = new BlockSupportImpl(file.getProject()).reparseRange( file, TextRange.allOf(fileText), fileText, new EmptyProgressIndicator(), fileText); diffLog.performActualPsiChange(file); TestCase.assertEquals(psiToStringDefault, DebugUtil.psiToString(file, false, false)); }
@NotNull private DiffLog merge(@NotNull final ASTNode oldRoot, @NotNull StartMarker newRoot, @NotNull CharSequence lastCommittedText) { DiffLog diffLog = new DiffLog(); DiffTreeChangeBuilder<ASTNode, LighterASTNode> builder = new ConvertFromTokensToASTBuilder(newRoot, diffLog); MyTreeStructure treeStructure = new MyTreeStructure(newRoot, null); ShallowNodeComparator<ASTNode, LighterASTNode> comparator = new MyComparator(getUserDataUnprotected(CUSTOM_COMPARATOR), treeStructure); ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator(); BlockSupportImpl.diffTrees(oldRoot, builder, comparator, treeStructure, indicator == null ? new EmptyProgressIndicator() : indicator, lastCommittedText); return diffLog; }
public static void doActualPsiChange(@NotNull final PsiFile file, @NotNull final DiffLog diffLog) { CodeStyleManager.getInstance(file.getProject()).performActionWithFormatterDisabled(new Runnable() { @Override public void run() { synchronized (PsiLock.LOCK) { file.getViewProvider().beforeContentsSynchronized(); final Document document = file.getViewProvider().getDocument(); PsiDocumentManagerBase documentManager = (PsiDocumentManagerBase)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); } } } }); }
private void assertAfterCommit(@NotNull Document document, @NotNull final PsiFile file, @NotNull FileElement myTreeElementBeingReparsedSoItWontBeCollected) { if (myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() != document.getTextLength()) { final String documentText = document.getText(); String fileText = file.getText(); LOG.error("commitDocument left PSI inconsistent: " + file + "; file len=" + myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() + "; doc len=" + document.getTextLength() + "; doc.getText() == file.getText(): " + Comparing.equal(fileText, documentText), new Attachment("file psi text", fileText), new Attachment("old text", documentText)); file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, Boolean.TRUE); try { BlockSupport blockSupport = BlockSupport.getInstance(file.getProject()); final DiffLog diffLog = blockSupport.reparseRange(file, new TextRange(0, documentText.length()), documentText, createProgressIndicator(), myTreeElementBeingReparsedSoItWontBeCollected.getText()); doActualPsiChange(file, diffLog); if (myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() != document.getTextLength()) { LOG.error("PSI is broken beyond repair in: " + file); } } finally { file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, null); } } }
private void reparseFile(final PsiFile file, String newText, String oldText) { if (oldText.equals(newText)) return; PsiToDocumentSynchronizer synchronizer =((PsiDocumentManagerBase)PsiDocumentManager.getInstance(myProject)).getSynchronizer(); int changeStart = StringUtil.commonPrefixLength(oldText, newText); int changeEnd = oldText.length() - StringUtil.commonSuffixLength(oldText, newText); TextRange changedPsiRange = DocumentCommitProcessor.getChangedPsiRange(file, changeStart, changeEnd, newText.length()); final DiffLog log = BlockSupport.getInstance(myProject).reparseRange(file, changedPsiRange, newText, new EmptyProgressIndicator()); synchronizer.setIgnorePsiEvents(true); try { CodeStyleManager.getInstance(file.getProject()).performActionWithFormatterDisabled(new Runnable() { @Override public void run() { runTransaction(new PomTransactionBase(file, getModelAspect(TreeAspect.class)) { @Nullable @Override public PomModelEvent runInner() throws IncorrectOperationException { return new TreeAspectEvent(PomModelImpl.this, log.performActualPsiChange(file)); } }); } }); } finally { synchronizer.setIgnorePsiEvents(false); } }
@NotNull private DiffLog merge(@NotNull final ASTNode oldRoot, @NotNull StartMarker newRoot) { DiffLog diffLog = new DiffLog(); final ConvertFromTokensToASTBuilder builder = new ConvertFromTokensToASTBuilder(newRoot, diffLog); final MyTreeStructure treeStructure = new MyTreeStructure(newRoot, null); final MyComparator comparator = new MyComparator(getUserDataUnprotected(CUSTOM_COMPARATOR), treeStructure); final ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator(); BlockSupportImpl.diffTrees(oldRoot, builder, comparator, treeStructure, indicator); return diffLog; }
public static void doActualPsiChange(@NotNull final PsiFile file, @NotNull final DiffLog diffLog) { file.getViewProvider().beforeContentsSynchronized(); try { final Document document = file.getViewProvider().getDocument(); PsiDocumentManagerBase documentManager = (PsiDocumentManagerBase)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); } }
@Nonnull private DiffLog merge(@Nonnull final ASTNode oldRoot, @Nonnull StartMarker newRoot, @Nonnull CharSequence lastCommittedText) { DiffLog diffLog = new DiffLog(); DiffTreeChangeBuilder<ASTNode, LighterASTNode> builder = new ConvertFromTokensToASTBuilder(newRoot, diffLog); MyTreeStructure treeStructure = new MyTreeStructure(newRoot, null); ShallowNodeComparator<ASTNode, LighterASTNode> comparator = new MyComparator(getUserDataUnprotected(CUSTOM_COMPARATOR), treeStructure); ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator(); BlockSupportImpl.diffTrees(oldRoot, builder, comparator, treeStructure, indicator == null ? new EmptyProgressIndicator() : indicator, lastCommittedText); return diffLog; }
@Nonnull public abstract DiffLog reparseRange(@Nonnull PsiFile file, @Nonnull FileASTNode oldFileNode, @Nonnull TextRange changedPsiRange, @Nonnull CharSequence newText, @Nonnull ProgressIndicator progressIndicator, @Nonnull CharSequence lastCommittedText) throws IncorrectOperationException;
private static void assertAfterCommit(@Nonnull Document document, @Nonnull final PsiFile file, @Nonnull FileElement oldFileNode) { if (oldFileNode.getTextLength() != document.getTextLength()) { final String documentText = document.getText(); String fileText = file.getText(); boolean sameText = Comparing.equal(fileText, documentText); LOG.error("commitDocument() left PSI inconsistent: " + DebugUtil.diagnosePsiDocumentInconsistency(file, document) + "; node.length=" + oldFileNode.getTextLength() + "; doc.text" + (sameText ? "==" : "!=") + "file.text" + "; file name:" + file.getName() + "; type:" + file.getFileType() + "; lang:" + file.getLanguage()); file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, Boolean.TRUE); try { BlockSupport blockSupport = BlockSupport.getInstance(file.getProject()); final DiffLog diffLog = blockSupport.reparseRange(file, file.getNode(), new TextRange(0, documentText.length()), documentText, createProgressIndicator(), oldFileNode.getText()); diffLog.doActualPsiChange(file); if (oldFileNode.getTextLength() != document.getTextLength()) { LOG.error("PSI is broken beyond repair in: " + file); } } finally { file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, null); } } }
private static void mergePsi(@Nonnull PsiFile oldFile, @Nonnull ASTNode oldFileNode, @Nonnull PsiFile injectedPsi, @Nonnull ASTNode injectedNode) { if (!oldFile.textMatches(injectedPsi)) { InjectedFileViewProvider oldViewProvider = (InjectedFileViewProvider)oldFile.getViewProvider(); oldViewProvider.performNonPhysically(() -> { DebugUtil.performPsiModification("injected tree diff", () -> { final DiffLog diffLog = BlockSupportImpl.mergeTrees((PsiFileImpl)oldFile, oldFileNode, injectedNode, new DaemonProgressIndicator(), oldFileNode.getText()); diffLog.doActualPsiChange(oldFile); }); }); } }
@NotNull public abstract DiffLog reparseRange(@NotNull PsiFile file, @NotNull TextRange changedPsiRange, @NotNull CharSequence newText, @NotNull ProgressIndicator progressIndicator, @NotNull CharSequence lastCommittedText) throws IncorrectOperationException;
public ReparsedSuccessfullyException(@NotNull DiffLog diffLog) { myDiffLog = diffLog; }
@NotNull public DiffLog getDiffLog() { return myDiffLog; }
@NotNull public abstract DiffLog reparseRange(@NotNull PsiFile file, TextRange changedPsiRange, @NotNull CharSequence newText, @NotNull ProgressIndicator progressIndicator) throws IncorrectOperationException;
private void assertAfterCommit(Document document, final PsiFile file, String oldPsiText, FileElement myTreeElementBeingReparsedSoItWontBeCollected) { if (myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() != document.getTextLength()) { final String documentText = document.getText(); if (ApplicationManager.getApplication().isInternal()) { String fileText = file.getText(); LOG.error("commitDocument left PSI inconsistent; file len=" + myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() + "; doc len=" + document.getTextLength() + "; doc.getText() == file.getText(): " + Comparing.equal(fileText, documentText) + ";\n file psi text=" + fileText + ";\n doc text=" + documentText + ";\n old psi file text=" + oldPsiText); } else { LOG.error("commitDocument left PSI inconsistent: " + file); } file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, Boolean.TRUE); try { BlockSupport blockSupport = BlockSupport.getInstance(file.getProject()); final DiffLog diffLog = blockSupport.reparseRange(file, new TextRange(0, documentText.length()), documentText, createProgressIndicator()); CodeStyleManager.getInstance(file.getProject()).performActionWithFormatterDisabled(new Runnable() { @Override public void run() { synchronized (PsiLock.LOCK) { doActualPsiChange(file, diffLog); } } }); if (myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() != document.getTextLength()) { LOG.error("PSI is broken beyond repair in: " + file); } } finally { file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, null); } } }
public ReparsedSuccessfullyException(@Nonnull DiffLog diffLog) { myDiffLog = diffLog; }
@Nonnull public DiffLog getDiffLog() { return myDiffLog; }
@Nonnull private static BooleanRunnable doCommit(@Nonnull final CommitTask task, @Nonnull final PsiFile file, @Nonnull final FileASTNode oldFileNode, @Nonnull ProperTextRange changedPsiRange, @Nonnull List<BooleanRunnable> outReparseInjectedProcessors) { Document document = task.getDocument(); final CharSequence newDocumentText = document.getImmutableCharSequence(); final Boolean data = document.getUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY); if (data != null) { document.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, null); file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, data); } Trinity<DiffLog, ASTNode, ASTNode> result = BlockSupportImpl.reparse(file, oldFileNode, changedPsiRange, newDocumentText, task.indicator, task.myLastCommittedText); DiffLog diffLog = result.getFirst(); ASTNode oldRoot = result.getSecond(); ASTNode newRoot = result.getThird(); PsiDocumentManagerBase documentManager = (PsiDocumentManagerBase)PsiDocumentManager.getInstance(task.project); List<BooleanRunnable> injectedRunnables = documentManager.reparseChangedInjectedFragments(document, file, changedPsiRange, task.indicator, oldRoot, newRoot); outReparseInjectedProcessors.addAll(injectedRunnables); return () -> { FileViewProvider viewProvider = file.getViewProvider(); Document document1 = task.getDocument(); if (!task.isStillValid() || ((PsiDocumentManagerBase)PsiDocumentManager.getInstance(file.getProject())).getCachedViewProvider(document1) != viewProvider) { return false; // optimistic locking failed } if (!ApplicationManager.getApplication().isWriteAccessAllowed()) { VirtualFile vFile = viewProvider.getVirtualFile(); LOG.error("Write action expected" + "; document=" + document1 + "; file=" + file + " of " + file.getClass() + "; file.valid=" + file.isValid() + "; file.eventSystemEnabled=" + viewProvider.isEventSystemEnabled() + "; viewProvider=" + viewProvider + " of " + viewProvider.getClass() + "; language=" + file.getLanguage() + "; vFile=" + vFile + " of " + vFile.getClass() + "; free-threaded=" + AbstractFileViewProvider.isFreeThreaded(viewProvider)); } diffLog.doActualPsiChange(file); assertAfterCommit(document1, file, (FileElement)oldFileNode); return true; }; }