simplify PsiToDocumentSynchronizer: process TreeChangeEvent directly instead of PSI events

towards uniform PSI-doc synchronization for physical and non-physical PSI, which is needed for rename to work in ModelBranch (LAB-51)

GitOrigin-RevId: eddfe0de35137fcc37a4a6854616d4194b10d772
This commit is contained in:
Peter Gromov
2020-07-15 06:53:31 +00:00
committed by intellij-monorepo-bot
parent 82ae7a22a6
commit c887211a58
6 changed files with 65 additions and 80 deletions
@@ -45,6 +45,18 @@ public class ChangeInfoImpl implements ChangeInfo {
return myOldChild;
}
public int getOffsetInParent() {
return myOffset;
}
public int getOldLength() {
return myOldLength;
}
public int getNewLength() {
return myNewLength;
}
@Nullable
public TreeElement getNewChild() {
return myNewChild;
@@ -14,11 +14,13 @@ import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@ApiStatus.Internal
public class TreeChangeEventImpl implements TreeChangeEvent{
private final Map<CompositeElement, TreeChangeImpl> myChangedElements = new LinkedHashMap<>();
private final MultiMap<CompositeElement, TreeChangeImpl> myChangesByAllParents = MultiMap.createSet();
@@ -127,12 +129,17 @@ public class TreeChangeEventImpl implements TreeChangeEvent{
}
public void fireEvents() {
Collection<TreeChangeImpl> changes = ContainerUtil.sorted(myChangedElements.values());
Collection<TreeChangeImpl> changes = getSortedChanges();
for (TreeChangeImpl change : changes) {
change.fireEvents((PsiFile)myFileElement.getPsi());
}
}
@NotNull
public List<TreeChangeImpl> getSortedChanges() {
return ContainerUtil.sorted(myChangedElements.values());
}
@Override
public void beforeNestedTransaction() {
// compute changes and remember them, to prevent lazy computation to happen in another transaction
@@ -9,7 +9,6 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.PomModelAspect;
import com.intellij.pom.event.PomModelEvent;
import com.intellij.pom.tree.TreeAspect;
import com.intellij.pom.tree.events.TreeChangeEvent;
import com.intellij.pom.tree.events.impl.TreeChangeEventImpl;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
@@ -29,12 +28,9 @@ public final class PsiEventWrapperAspect implements PomModelAspect {
@Override
public void update(PomModelEvent event) {
final TreeChangeEvent changeSet = (TreeChangeEvent)event.getChangeSet(myTreeAspect);
TreeChangeEventImpl changeSet = (TreeChangeEventImpl)event.getChangeSet(myTreeAspect);
if(changeSet == null) return;
sendAfterEvents(changeSet);
}
private static void sendAfterEvents(TreeChangeEvent changeSet) {
ASTNode rootElement = changeSet.getRootElement();
PsiFile file = (PsiFile)rootElement.getPsi();
if (!file.isPhysical()) {
@@ -43,7 +39,8 @@ public final class PsiEventWrapperAspect implements PomModelAspect {
return;
}
((TreeChangeEventImpl)changeSet).fireEvents();
((PsiDocumentManagerBase)PsiDocumentManager.getInstance(file.getProject())).getSynchronizer().processEvents(changeSet, file);
changeSet.fireEvents();
}
private static void promoteNonPhysicalChangesToDocument(ASTNode rootElement, PsiFile file) {
@@ -228,15 +228,12 @@ public class DiffLog implements DiffTreeChangeBuilder<ASTNode,ASTNode> {
@Override
void doActualPsiChange(@NotNull PsiFile file, @NotNull TreeChangeEventImpl event) {
PsiFileImpl fileImpl = (PsiFileImpl)file;
final int oldLength = myOldNode.getTextLength();
PsiManagerImpl manager = (PsiManagerImpl)fileImpl.getManager();
BlockSupportImpl.sendBeforeChildrenChangeEvent(manager, fileImpl, false);
event.addElementaryChange(myOldNode);
if (myOldNode.getFirstChildNode() != null) myOldNode.rawRemoveAllChildren();
final TreeElement firstChildNode = myNewNode.getFirstChildNode();
if (firstChildNode != null) myOldNode.rawAddChildren(firstChildNode);
fileImpl.calcTreeElement().setCharTable(myNewNode.getCharTable());
myOldNode.subtreeChanged();
BlockSupportImpl.sendAfterChildrenChangedEvent(manager,fileImpl, oldLength, false);
}
}
@@ -74,7 +74,6 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
myPsiManager = PsiManager.getInstance(project);
myDocumentCommitProcessor = ApplicationManager.getApplication().getService(DocumentCommitProcessor.class);
mySynchronizer = new PsiToDocumentSynchronizer(this, project.getMessageBus());
myPsiManager.addPsiTreeChangeListener(mySynchronizer, this);
}
@Override
@@ -3,6 +3,7 @@
package com.intellij.psi.impl;
import com.intellij.injected.editor.DocumentWindow;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
@@ -12,14 +13,23 @@ import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.pom.tree.events.impl.ChangeInfoImpl;
import com.intellij.pom.tree.events.impl.TreeChangeEventImpl;
import com.intellij.pom.tree.events.impl.TreeChangeImpl;
import com.intellij.psi.IgnorePsiEventsMarker;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiTreeChangeEvent;
import com.intellij.psi.impl.source.DummyHolder;
import com.intellij.psi.impl.source.tree.ForeignLeafPsiElement;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.text.ImmutableCharSequence;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
@@ -29,7 +39,7 @@ import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter {
public class PsiToDocumentSynchronizer {
private static final Logger LOG = Logger.getInstance(PsiToDocumentSynchronizer.class);
private static final Key<Boolean> PSI_DOCUMENT_ATOMIC_ACTION = Key.create("PSI_DOCUMENT_ATOMIC_ACTION");
@@ -83,12 +93,12 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter {
return document;
}
private void doSync(final @NotNull PsiTreeChangeEvent event, boolean force, final @NotNull DocSyncAction syncAction) {
private void doSync(@NotNull PsiTreeChangeEvent event, @NotNull DocSyncAction syncAction) {
if (!toProcessPsiEvent()) return;
final PsiFile psiFile = event.getFile();
if (!(psiFile instanceof PsiFileEx) || !((PsiFileEx)psiFile).isContentsLoaded()) return;
final DocumentEx document = getCachedDocument(psiFile, force);
DocumentEx document = getCachedDocument(psiFile, true);
if (document == null) return;
performAtomically(psiFile, () -> syncAction.syncDocument(document, (PsiTreeChangeEventImpl)event));
@@ -116,56 +126,6 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter {
}
}
@Override
public void beforeChildAddition(@NotNull PsiTreeChangeEvent event) {
checkPsiModificationAllowed(event);
}
@Override
public void beforeChildRemoval(@NotNull PsiTreeChangeEvent event) {
checkPsiModificationAllowed(event);
}
@Override
public void beforeChildReplacement(@NotNull PsiTreeChangeEvent event) {
checkPsiModificationAllowed(event);
}
@Override
public void beforeChildrenChange(@NotNull PsiTreeChangeEvent event) {
checkPsiModificationAllowed(event);
}
@Override
public void childAdded(final @NotNull PsiTreeChangeEvent event) {
if (!(event.getChild() instanceof ForeignLeafPsiElement)) {
doSync(event, false, (document, event1) -> insertString(document, event1.getOffset(), event1.getChild().getText()));
}
}
@Override
public void childRemoved(final @NotNull PsiTreeChangeEvent event) {
if (!(event.getChild() instanceof ForeignLeafPsiElement)) {
doSync(event, false, (document, event1) -> deleteString(document, event1.getOffset(), event1.getOffset() + event1.getOldLength()));
}
}
@Override
public void childReplaced(final @NotNull PsiTreeChangeEvent event) {
doSync(event, false, (document, event1) -> {
int oldLength = event1.getOldChild() instanceof ForeignLeafPsiElement ? 0 : event1.getOldLength();
String newText = event1.getNewChild() instanceof ForeignLeafPsiElement ? "" : event1.getNewChild().getText();
replaceString(document, event1.getOffset(), event1.getOffset() + oldLength, newText, event1.getNewChild());
});
}
@Override
public void childrenChanged(final @NotNull PsiTreeChangeEvent event) {
doSync(event, false, (document, event1) ->
replaceString(document, event1.getOffset(), event1.getOffset() + event1.getOldLength(), event1.getParent().getText(), event1
.getParent()));
}
private boolean myIgnorePsiEvents;
public void setIgnorePsiEvents(boolean ignorePsiEvents) {
myIgnorePsiEvents = ignorePsiEvents;
@@ -181,16 +141,13 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter {
@TestOnly
public void replaceString(@NotNull Document document, int startOffset, int endOffset, @NotNull String s) {
replaceString(document, startOffset, endOffset, s, null);
}
private void replaceString(@NotNull Document document, int startOffset, int endOffset, @NotNull String s, @Nullable PsiElement replacement) {
final DocumentChangeTransaction documentChangeTransaction = getTransaction(document);
if(documentChangeTransaction != null) {
documentChangeTransaction.replace(startOffset, endOffset - startOffset, s, replacement);
documentChangeTransaction.replace(startOffset, endOffset - startOffset, s, null);
}
}
@TestOnly
public void insertString(@NotNull Document document, int offset, @NotNull String s) {
final DocumentChangeTransaction documentChangeTransaction = getTransaction(document);
if(documentChangeTransaction != null){
@@ -198,13 +155,6 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter {
}
}
private void deleteString(@NotNull Document document, int startOffset, int endOffset){
final DocumentChangeTransaction documentChangeTransaction = getTransaction(document);
if(documentChangeTransaction != null){
documentChangeTransaction.replace(startOffset, endOffset - startOffset, "", null);
}
}
public void startTransaction(@NotNull Project project, @NotNull Document doc, @NotNull PsiElement scope) {
LOG.assertTrue(!project.isDisposed());
Pair<DocumentChangeTransaction, Integer> pair = myTransactionsMap.get(doc);
@@ -232,11 +182,12 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter {
fakeEvent.setParent(changeScope);
fakeEvent.setFile(changeScope);
checkPsiModificationAllowed(fakeEvent);
doSync(fakeEvent, true, (document1, event) -> doCommitTransaction(document1, documentChangeTransaction));
doSync(fakeEvent, (document1, event) -> doCommitTransaction(document1, documentChangeTransaction));
myBus.syncPublisher(PsiDocumentTransactionListener.TOPIC).transactionCompleted(document, changeScope);
}
catch (Throwable e) {
myPsiDocumentManager.forceReload(changeScope.getViewProvider().getVirtualFile(), changeScope.getViewProvider());
//noinspection ConstantConditions
ExceptionUtil.rethrowAllAsUnchecked(e);
}
finally {
@@ -280,6 +231,27 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter {
return myTransactionsMap.containsKey(document);
}
@ApiStatus.Internal
public void processEvents(@NotNull TreeChangeEventImpl changeSet, @NotNull PsiFile file) {
if (file instanceof DummyHolder || !toProcessPsiEvent()) return;
Document document = getCachedDocument(file, false);
DocumentChangeTransaction transaction = document == null ? null : getTransaction(document);
if (transaction == null) return;
for (TreeChangeImpl change : changeSet.getSortedChanges()) {
int parentStart = change.getChangedParent().getStartOffset();
for (ASTNode child : change.getAffectedChildren()) {
ChangeInfoImpl info = change.getChangeByChild(child);
TreeElement newChild = info.getNewChild();
PsiElement newPsi = newChild == null ? null : newChild.getPsi();
if (!(newPsi instanceof ForeignLeafPsiElement)) {
transaction.replace(info.getOffsetInParent() + parentStart, info.getOldLength(), newChild == null ? "" : newChild.getText(), newPsi);
}
}
}
}
public static class DocumentChangeTransaction{
private final TreeMap<TextRange, CharSequence> myAffectedFragments = new TreeMap<>(Comparator.comparingInt(TextRange::getStartOffset));
private final PsiFile myChangeScope;
@@ -290,11 +262,12 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter {
myPsiText = CharArrayUtil.createImmutableCharSequence(doc.getImmutableCharSequence());
}
@TestOnly
public @NotNull Map<TextRange, CharSequence> getAffectedFragments() {
return myAffectedFragments;
}
public void replace(int psiStart, int length, @NotNull String replace, @Nullable PsiElement replacement) {
void replace(int psiStart, int length, @NotNull String replace, @Nullable PsiElement replacement) {
// calculating fragment
// minimize replace
int start = 0;