diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiModificationTrackerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/PsiModificationTrackerImpl.java index 1fc5d46bfcf7..fa223fd4b073 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiModificationTrackerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiModificationTrackerImpl.java @@ -15,19 +15,28 @@ */ package com.intellij.psi.impl; +import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.application.TransactionGuardImpl; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.ModificationTracker; +import com.intellij.openapi.util.SimpleModificationTracker; import com.intellij.openapi.util.registry.Registry; import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.psi.PsiTreeChangeEvent; import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.util.containers.ConcurrentFactoryMap; import com.intellij.util.messages.MessageBus; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import static com.intellij.psi.impl.PsiTreeChangeEventImpl.PsiEventType.CHILD_MOVED; @@ -40,6 +49,7 @@ import static com.intellij.psi.impl.PsiTreeChangeEventImpl.PsiEventType.PROPERTY public class PsiModificationTrackerImpl implements PsiModificationTracker, PsiTreeChangePreprocessor { private final boolean myEnableCodeBlockTracker = Registry.is("psi.modification.tracker.code-block"); private final boolean myEnableJavaStructureTracker = Registry.is("psi.modification.tracker.java-structure"); + private final boolean myEnableLanguageTracker = Registry.is("psi.modification.tracker.per-language"); private final AtomicLong myModificationCount = new AtomicLong(0); private final AtomicLong myOutOfCodeBlockModificationCount = myEnableCodeBlockTracker ? new AtomicLong(0) : myModificationCount; @@ -48,6 +58,9 @@ public class PsiModificationTrackerImpl implements PsiModificationTracker, PsiTr private final ModificationTracker myOutOfCodeBlockModificationTracker = myEnableCodeBlockTracker ? () -> getOutOfCodeBlockModificationCount() : this; private final ModificationTracker myJavaStructureModificationTracker = myEnableJavaStructureTracker ? () -> getJavaStructureModificationCount() : this; + private final Map myLanguageTrackers = + ConcurrentFactoryMap.createMap(language -> new SimpleModificationTracker()); + private final Listener myPublisher; public PsiModificationTrackerImpl(Project project) { @@ -94,6 +107,8 @@ public class PsiModificationTrackerImpl implements PsiModificationTracker, PsiTr return; } + incLanguageTrackers(event); + PsiTreeChangeEventImpl.PsiEventType code = event.getCode(); boolean outOfCodeBlock = code == PROPERTY_CHANGED ? event.getPropertyName() == PsiTreeChangeEvent.PROP_UNLOADED_PSI : @@ -112,6 +127,26 @@ public class PsiModificationTrackerImpl implements PsiModificationTracker, PsiTr return !PsiTreeChangeEvent.PROP_WRITABLE.equals(event.getPropertyName()); } + protected void incLanguageTrackers(@NotNull PsiTreeChangeEventImpl event) { + if (!myEnableLanguageTracker) return; + incLanguageModificationCount(Language.ANY); + for (PsiElement o : new PsiElement[]{ + event.getFile(), event.getParent(), event.getOldParent(), event.getNewParent(), + event.getElement(), event.getChild(), event.getOldChild(), event.getNewChild() + }) { + PsiFile file = o instanceof PsiFile ? (PsiFile)o : null; + if (file == null) { + Language language = o == null ? null : o.getLanguage(); + incLanguageModificationCount(language); + } + else { + for (Language language : file.getViewProvider().getLanguages()) { + incLanguageModificationCount(language); + } + } + } + } + @Override public long getModificationCount() { return myModificationCount.get(); @@ -138,4 +173,31 @@ public class PsiModificationTrackerImpl implements PsiModificationTracker, PsiTr public ModificationTracker getJavaStructureModificationTracker() { return myJavaStructureModificationTracker; } + + @ApiStatus.Experimental + public void incLanguageModificationCount(@Nullable Language language) { + if (language == null) return; + ((SimpleModificationTracker)myLanguageTrackers.get(language)).incModificationCount(); + } + + @ApiStatus.Experimental + @NotNull + public ModificationTracker forLanguage(@NotNull Language language) { + if (!myEnableLanguageTracker) return this; + return myLanguageTrackers.get(language); + } + + @ApiStatus.Experimental + @NotNull + public ModificationTracker forLanguages(@NotNull Condition condition) { + if (!myEnableLanguageTracker) return this; + return () -> { + long result = 0; + for (Language l : myLanguageTrackers.keySet()) { + if (condition.value(l)) continue; + result += myLanguageTrackers.get(l).getModificationCount(); + } + return result; + }; + } } diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 6e00740200fc..163ce7250e09 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -371,6 +371,8 @@ psi.modification.tracker.code-block=true psi.modification.tracker.code-block.description=Controls out-of-code-block PSI modification tracker optimization (requires project reopening) psi.modification.tracker.java-structure=true psi.modification.tracker.java-structure.description=Controls java-structure PSI modification tracker optimization (requires project reopening) +psi.modification.tracker.per-language=false +psi.modification.tracker.per-language.description=Controls per-language PSI modification tracker optimization (requires project reopening) ide.suggest.file.when.creating.filename.like.directory=true ide.suggest.file.when.creating.filename.like.directory.description=When creating a directory named foo.txt, suggest to create a file with such a name instead