/** * 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; }
@Override @NotNull public DiffLog reparseRange(@NotNull final PsiFile file, TextRange changedPsiRange, @NotNull final CharSequence newFileText, @NotNull final ProgressIndicator indicator) { final PsiFileImpl fileImpl = (PsiFileImpl)file; Project project = fileImpl.getProject(); final FileElement treeFileElement = fileImpl.getTreeElement(); final CharTable charTable = treeFileElement.getCharTable(); final int textLength = newFileText.length(); int lengthShift = textLength - treeFileElement.getTextLength(); if (treeFileElement.getElementType() instanceof ITemplateDataElementType || isTooDeep(file)) { // unable to perform incremental reparse for template data in JSP, or in exceptionally deep trees return makeFullParse(treeFileElement, newFileText, textLength, fileImpl, indicator); } final ASTNode leafAtStart = treeFileElement.findLeafElementAt(Math.max(0, changedPsiRange.getStartOffset() - 1)); final ASTNode leafAtEnd = treeFileElement.findLeafElementAt(changedPsiRange.getEndOffset()); ASTNode node = leafAtStart != null && leafAtEnd != null ? TreeUtil.findCommonParent(leafAtStart, leafAtEnd) : treeFileElement; 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())) { final int start = textRange.getStartOffset(); final int end = start + textRange.getLength() + lengthShift; assertFileLength(file, newFileText, node, elementType, start, end); CharSequence newTextStr = newFileText.subSequence(start, end); if (reparseable.isParsable(newTextStr, baseLanguage, project)) { ASTNode chameleon = reparseable.createNode(newTextStr); if (chameleon != null) { DummyHolder holder = DummyHolderFactory.createHolder(fileImpl.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 mergeTrees(fileImpl, node, chameleon, indicator); } } } } node = node.getTreeParent(); } return makeFullParse(node, newFileText, textLength, fileImpl, indicator); }
/** * Find ast node that could be reparsed incrementally * * @return Pair (target reparseable node, new replacement node) * or {@code null} if can't parse incrementally. */ @Nullable public static Couple<ASTNode> findReparseableRoots(@Nonnull PsiFileImpl file, @Nonnull FileASTNode oldFileNode, @Nonnull TextRange changedPsiRange, @Nonnull CharSequence newFileText) { Project project = file.getProject(); final FileElement fileElement = (FileElement)oldFileNode; 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(file, 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; }