diff --git a/java/java-impl/src/com/intellij/psi/impl/JavaCodeBlockModificationListener.java b/java/java-impl/src/com/intellij/psi/impl/JavaCodeBlockModificationListener.java new file mode 100644 index 000000000000..941032b535e7 --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/impl/JavaCodeBlockModificationListener.java @@ -0,0 +1,130 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.jsp.jspXml.JspDirective; +import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.psi.xml.XmlFile; + +public class JavaCodeBlockModificationListener implements PsiTreeChangePreprocessor { + private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.JavaCodeBlockModificationListener"); + + private final PsiModificationTrackerImpl myModificationTracker; + + public JavaCodeBlockModificationListener(final PsiModificationTracker modificationTracker) { + myModificationTracker = (PsiModificationTrackerImpl) modificationTracker; + } + + public void treeChanged(final PsiTreeChangeEventImpl event) { + switch (event.getCode()) { + case BEFORE_CHILDREN_CHANGE: + case BEFORE_PROPERTY_CHANGE: + case BEFORE_CHILD_MOVEMENT: + case BEFORE_CHILD_REPLACEMENT: + case BEFORE_CHILD_ADDITION: + case BEFORE_CHILD_REMOVAL: + break; + + case CHILD_ADDED: + case CHILD_REMOVED: + case CHILD_REPLACED: + processChange(event.getParent(), event.getOldChild(), event.getChild()); + break; + + case CHILDREN_CHANGED: + // general childrenChanged() event after each change + if (!event.isGenericChildrenChange()) { + processChange(event.getParent(), event.getParent(), null); + } + break; + + case CHILD_MOVED: + case PROPERTY_CHANGED: + myModificationTracker.incCounter(); + break; + + default: + LOG.error("Unknown code:" + event.getCode()); + break; + } + } + + private void processChange(final PsiElement parent, final PsiElement child1, final PsiElement child2) { + try { + if (!isInsideCodeBlock(parent)) { + if (parent != null && isClassOwner(parent.getContainingFile()) || isClassOwner(child1) || isClassOwner(child2) || isSourceDir(parent)) { + myModificationTracker.incCounter(); + } + else { + myModificationTracker.incOutOfCodeBlockModificationCounter(); + } + return; + } + + if (containsClassesInside(child1) || child2 != child1 && containsClassesInside(child2)) { + myModificationTracker.incCounter(); + } + } + catch (PsiInvalidElementAccessException e) { + myModificationTracker.incCounter(); // Shall not happen actually, just a pre-release paranoia + } + } + + private static boolean isSourceDir(PsiElement element) { + return element instanceof PsiDirectory && JavaDirectoryService.getInstance().getPackage((PsiDirectory)element) != null; + } + + private static boolean isClassOwner(final PsiElement element) { + return element instanceof PsiClassOwner && !(element instanceof XmlFile) || element instanceof JspDirective; + } + + private static boolean containsClassesInside(final PsiElement element) { + if (element == null) return false; + if (element instanceof PsiClass) return true; + + PsiElement child = element.getFirstChild(); + while (child != null) { + if (containsClassesInside(child)) return true; + child = child.getNextSibling(); + } + + return false; + } + + private static boolean isInsideCodeBlock(PsiElement element) { + if (element instanceof PsiFileSystemItem) { + return false; + } + + if (element == null || element.getParent() == null) return true; + + PsiElement parent = element; + while (true) { + if (parent instanceof PsiFile || parent instanceof PsiDirectory || parent == null) { + return false; + } + if (parent instanceof PsiClass) return false; // anonymous or local class + if (parent instanceof PsiModifiableCodeBlock) { + if (!((PsiModifiableCodeBlock)parent).shouldChangeModificationCount(element)) { + return true; + } + } + parent = parent.getParent(); + } + } +} diff --git a/java/java-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java b/java/java-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java index 78e7abb498b8..e4fc278bd74c 100644 --- a/java/java-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java @@ -31,12 +31,10 @@ import com.intellij.psi.impl.file.impl.JavaFileManager; import com.intellij.psi.impl.source.DummyHolderFactory; import com.intellij.psi.impl.source.JavaDummyHolder; import com.intellij.psi.impl.source.JavaDummyHolderFactory; -import com.intellij.psi.impl.source.jsp.jspXml.JspDirective; import com.intellij.psi.impl.source.resolve.FileContextUtil; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiModificationTracker; -import com.intellij.psi.xml.XmlFile; import com.intellij.util.ConcurrencyUtil; import com.intellij.util.Processor; import com.intellij.util.SmartList; @@ -74,8 +72,7 @@ public class JavaPsiFacadeImpl extends JavaPsiFacadeEx { ContainerUtil.addAll(elementFinders, myProject.getExtensions(PsiElementFinder.EP_NAME)); myElementFinders = elementFinders.toArray(new PsiElementFinder[elementFinders.size()]); - final PsiModificationTrackerImpl modificationTracker = (PsiModificationTrackerImpl)psiManager.getModificationTracker(); - psiManager.addTreeChangePreprocessor(new JavaCodeBlockModificationListener(modificationTracker)); + final PsiModificationTracker modificationTracker = psiManager.getModificationTracker(); bus.connect().subscribe(PsiModificationTracker.TOPIC, new PsiModificationTracker.Listener() { private long lastTimeSeen = -1L; @@ -397,110 +394,4 @@ public class JavaPsiFacadeImpl extends JavaPsiFacadeEx { public void setAssertOnFileLoadingFilter(final VirtualFileFilter filter) { ((PsiManagerImpl)PsiManager.getInstance(myProject)).setAssertOnFileLoadingFilter(filter); } - - private static class JavaCodeBlockModificationListener implements PsiTreeChangePreprocessor { - private final PsiModificationTrackerImpl myModificationTracker; - - private JavaCodeBlockModificationListener(final PsiModificationTrackerImpl modificationTracker) { - myModificationTracker = modificationTracker; - } - - public void treeChanged(final PsiTreeChangeEventImpl event) { - switch (event.getCode()) { - case BEFORE_CHILDREN_CHANGE: - case BEFORE_PROPERTY_CHANGE: - case BEFORE_CHILD_MOVEMENT: - case BEFORE_CHILD_REPLACEMENT: - case BEFORE_CHILD_ADDITION: - case BEFORE_CHILD_REMOVAL: - break; - - case CHILD_ADDED: - case CHILD_REMOVED: - case CHILD_REPLACED: - processChange(event.getParent(), event.getOldChild(), event.getChild()); - break; - - case CHILDREN_CHANGED: - // general childrenChanged() event after each change - if (!event.isGenericChildrenChange()) { - processChange(event.getParent(), event.getParent(), null); - } - break; - - case CHILD_MOVED: - case PROPERTY_CHANGED: - myModificationTracker.incCounter(); - break; - - default: - LOG.error("Unknown code:" + event.getCode()); - break; - } - } - - private void processChange(final PsiElement parent, final PsiElement child1, final PsiElement child2) { - try { - if (!isInsideCodeBlock(parent)) { - if (parent != null && isClassOwner(parent.getContainingFile()) || isClassOwner(child1) || isClassOwner(child2) || isSourceDir(parent)) { - myModificationTracker.incCounter(); - } - else { - myModificationTracker.incOutOfCodeBlockModificationCounter(); - } - return; - } - - if (containsClassesInside(child1) || child2 != child1 && containsClassesInside(child2)) { - myModificationTracker.incCounter(); - } - } - catch (PsiInvalidElementAccessException e) { - myModificationTracker.incCounter(); // Shall not happen actually, just a pre-release paranoia - } - } - - private static boolean isSourceDir(PsiElement element) { - return element instanceof PsiDirectory && JavaDirectoryService.getInstance().getPackage((PsiDirectory)element) != null; - } - - private static boolean isClassOwner(final PsiElement element) { - return element instanceof PsiClassOwner && !(element instanceof XmlFile) || element instanceof JspDirective; - } - - private static boolean containsClassesInside(final PsiElement element) { - if (element == null) return false; - if (element instanceof PsiClass) return true; - - PsiElement child = element.getFirstChild(); - while (child != null) { - if (containsClassesInside(child)) return true; - child = child.getNextSibling(); - } - - return false; - } - - private static boolean isInsideCodeBlock(PsiElement element) { - if (element instanceof PsiFileSystemItem) { - return false; - } - - if (element == null || element.getParent() == null) return true; - - PsiElement parent = element; - while (true) { - if (parent instanceof PsiFile || parent instanceof PsiDirectory || parent == null) { - return false; - } - if (parent instanceof PsiClass) return false; // anonymous or local class - if (parent instanceof PsiModifiableCodeBlock) { - if (!((PsiModifiableCodeBlock)parent).shouldChangeModificationCount(element)) { - return true; - } - } - parent = parent.getParent(); - } - } - } } diff --git a/platform/core-api/src/com/intellij/psi/util/PsiModificationTracker.java b/platform/core-api/src/com/intellij/psi/util/PsiModificationTracker.java index e0e58be320e1..a3d72a9d48ab 100644 --- a/platform/core-api/src/com/intellij/psi/util/PsiModificationTracker.java +++ b/platform/core-api/src/com/intellij/psi/util/PsiModificationTracker.java @@ -15,11 +15,22 @@ */ package com.intellij.psi.util; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.ModificationTracker; import com.intellij.util.messages.Topic; public interface PsiModificationTracker extends ModificationTracker { + class SERVICE { + private SERVICE() { + } + + public static PsiModificationTracker getInstance(Project project) { + return ServiceManager.getService(project, PsiModificationTracker.class); + } + } + /** * This key can be passed as a dependency in a {@link com.intellij.psi.util.CachedValueProvider}. * @see #getModificationCount() diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiManagerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/PsiManagerImpl.java index 213b8ad2c56f..80d968e1852e 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiManagerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiManagerImpl.java @@ -21,6 +21,7 @@ import com.intellij.lang.PsiBuilderFactory; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.openapi.project.Project; @@ -43,6 +44,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; +import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -54,7 +56,6 @@ public class PsiManagerImpl extends PsiManagerEx { private final MessageBus myMessageBus; private final FileManager myFileManager; - private final PsiModificationTrackerImpl myModificationTracker; private final List myTreeChangePreprocessors = ContainerUtil.createEmptyCOWList(); private final List myTreeChangeListeners = ContainerUtil.createEmptyCOWList(); @@ -85,8 +86,8 @@ public class PsiManagerImpl extends PsiManagerEx { myFileManager = isProjectDefault ? new EmptyFileManager(this) : new FileManagerImpl(this, fileDocumentManager, excludedFileIndex); - myModificationTracker = new PsiModificationTrackerImpl(myProject); - myTreeChangePreprocessors.add(myModificationTracker); + myTreeChangePreprocessors.add((PsiTreeChangePreprocessor) PsiModificationTracker.SERVICE.getInstance(project)); + Collections.addAll(myTreeChangePreprocessors, Extensions.getExtensions(PsiTreeChangePreprocessor.EP_NAME, myProject)); Disposer.register(project, new Disposable() { @Override @@ -504,7 +505,7 @@ public class PsiManagerImpl extends PsiManagerEx { @NotNull public PsiModificationTracker getModificationTracker() { - return myModificationTracker; + return PsiModificationTracker.SERVICE.getInstance(myProject); } public void startBatchFilesProcessingMode() { diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiTreeChangePreprocessor.java b/platform/core-impl/src/com/intellij/psi/impl/PsiTreeChangePreprocessor.java index 63ad0cb8a5e4..35224e14613f 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiTreeChangePreprocessor.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiTreeChangePreprocessor.java @@ -16,9 +16,13 @@ package com.intellij.psi.impl; +import com.intellij.openapi.extensions.ExtensionPointName; + /** * @author yole */ public interface PsiTreeChangePreprocessor { + ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.psi.treeChangePreprocessor"); + void treeChanged(PsiTreeChangeEventImpl event); } diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index bdce69e6c294..cb6beb83ed70 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -508,4 +508,8 @@ interface="com.intellij.psi.impl.source.tree.TreeCopyHandler"/> + + diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index a77bbeeb54bf..0b369c9c99af 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -698,4 +698,7 @@ + + diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 816461aee383..72765227fa29 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -347,6 +347,8 @@ + +