From 098d0ef15d2651dee5aaf86ebc825eb9c027b74f Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 11 Oct 2019 11:24:35 +0200 Subject: [PATCH] Unloadable PsiReferenceContributor GitOrigin-RevId: 762a3d5a70c32b3dcba601167ef55195fc2ef8ad --- .../src/com/intellij/lang/Language.java | 4 ++ .../intellij/psi/PsiReferenceContributor.java | 3 +- .../reference/ReferenceProvidersRegistry.java | 18 +---- .../reference/NamedObjectProviderBinding.java | 4 ++ .../reference/PsiReferenceRegistrarImpl.java | 40 +++++++++-- .../ReferenceProvidersRegistryImpl.java | 67 ++++++++++++++----- .../reference/SimpleProviderBinding.java | 4 ++ .../reference/TrackingReferenceRegistrar.java | 26 +++++++ .../fileTypes/impl/FileTypeManagerImpl.java | 2 - .../src/META-INF/LangExtensionPoints.xml | 6 +- 10 files changed, 130 insertions(+), 44 deletions(-) rename platform/{core-impl => core-api}/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistry.java (77%) create mode 100644 platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/TrackingReferenceRegistrar.java diff --git a/platform/core-api/src/com/intellij/lang/Language.java b/platform/core-api/src/com/intellij/lang/Language.java index 32fb5a5f67c6..1be45576f560 100644 --- a/platform/core-api/src/com/intellij/lang/Language.java +++ b/platform/core-api/src/com/intellij/lang/Language.java @@ -7,6 +7,8 @@ import com.intellij.openapi.fileTypes.FileTypeRegistry; import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; +import com.intellij.psi.tree.IElementType; import com.intellij.util.ArrayUtilRt; import com.intellij.util.ConcurrencyUtil; import com.intellij.util.containers.ContainerUtil; @@ -108,6 +110,8 @@ public abstract class Language extends UserDataHolderBase { } public static void unregisterLanguage(@NotNull Language language) { + IElementType.unregisterElementTypes(language); + ReferenceProvidersRegistry.getInstance().unloadRegistrar(language); ourRegisteredLanguages.remove(language.getClass()); ourRegisteredIDs.remove(language.getID()); for (String mimeType : language.getMimeTypes()) { diff --git a/platform/core-api/src/com/intellij/psi/PsiReferenceContributor.java b/platform/core-api/src/com/intellij/psi/PsiReferenceContributor.java index 9708eb75ee95..493662fcb396 100644 --- a/platform/core-api/src/com/intellij/psi/PsiReferenceContributor.java +++ b/platform/core-api/src/com/intellij/psi/PsiReferenceContributor.java @@ -17,6 +17,7 @@ package com.intellij.psi; import com.intellij.openapi.Disposable; import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.util.KeyedLazyInstance; import org.jetbrains.annotations.NotNull; /** @@ -42,7 +43,7 @@ import org.jetbrains.annotations.NotNull; * @see PsiReferenceProviderBean */ public abstract class PsiReferenceContributor implements Disposable { - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.psi.referenceContributor"); + public static final ExtensionPointName> EP_NAME = ExtensionPointName.create("com.intellij.psi.referenceContributor"); public abstract void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar); diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistry.java b/platform/core-api/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistry.java similarity index 77% rename from platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistry.java rename to platform/core-api/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistry.java index 307f9391e904..8d10115b6b45 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistry.java +++ b/platform/core-api/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistry.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 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. - */ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.psi.impl.source.resolve.reference; @@ -60,6 +46,8 @@ public abstract class ReferenceProvidersRegistry { return getInstance().doGetReferencesFromProviders(context, hints); } + public abstract void unloadRegistrar(@NotNull Language language); + @NotNull protected abstract PsiReference[] doGetReferencesFromProviders(@NotNull PsiElement context, @NotNull PsiReferenceService.Hints hints); } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/NamedObjectProviderBinding.java b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/NamedObjectProviderBinding.java index 0a7f18d50531..4333e137c9a0 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/NamedObjectProviderBinding.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/NamedObjectProviderBinding.java @@ -74,6 +74,10 @@ public abstract class NamedObjectProviderBinding implements ProviderBinding { } } + public boolean isEmpty() { + return myNamesToProvidersMap.isEmpty() && myNamesToProvidersMapInsensitive.isEmpty(); + } + @Nullable protected abstract String getName(@NotNull PsiElement position); diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/PsiReferenceRegistrarImpl.java b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/PsiReferenceRegistrarImpl.java index 273f55e1e602..4fdb8bf6e8b0 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/PsiReferenceRegistrarImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/PsiReferenceRegistrarImpl.java @@ -1,8 +1,10 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.psi.impl.source.resolve.reference; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Disposer; import com.intellij.patterns.*; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReferenceProvider; @@ -14,6 +16,7 @@ import com.intellij.util.containers.ConcurrentFactoryMap; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Collections; @@ -57,7 +60,14 @@ public class PsiReferenceRegistrarImpl extends PsiReferenceRegistrar { public void registerReferenceProvider(@NotNull ElementPattern pattern, @NotNull PsiReferenceProvider provider, double priority) { - if (myInitialized && !ApplicationManager.getApplication().isUnitTestMode()) { + registerReferenceProvider(pattern, provider, priority, null); + } + + public void registerReferenceProvider(@NotNull ElementPattern pattern, + @NotNull PsiReferenceProvider provider, + double priority, + @Nullable Disposable parentDisposable) { + if (myInitialized && !ApplicationManager.getApplication().isUnitTestMode() && parentDisposable == null) { LOG.error("Reference provider registration is only allowed from PsiReferenceContributor"); } @@ -72,12 +82,12 @@ public class PsiReferenceRegistrarImpl extends PsiReferenceRegistrar { for (PatternCondition condition1 : conditions1) { if (condition1 instanceof ValuePatternCondition) { final Collection strings = ((ValuePatternCondition)condition1).getValues(); - registerNamedReferenceProvider(ArrayUtilRt.toStringArray(strings), nameCondition, scope, true, provider, priority, pattern); + registerNamedReferenceProvider(ArrayUtilRt.toStringArray(strings), nameCondition, scope, true, provider, priority, pattern, parentDisposable); return; } if (condition1 instanceof CaseInsensitiveValuePatternCondition) { final String[] strings = ((CaseInsensitiveValuePatternCondition)condition1).getValues(); - registerNamedReferenceProvider(strings, nameCondition, scope, false, provider, priority, pattern); + registerNamedReferenceProvider(strings, nameCondition, scope, false, provider, priority, pattern, parentDisposable); return; } } @@ -89,22 +99,31 @@ public class PsiReferenceRegistrarImpl extends PsiReferenceRegistrar { myBindingsMap.put(scope, providerBinding = new SimpleProviderBinding()); } providerBinding.registerProvider(provider, pattern, priority); + if (parentDisposable != null) { + Disposer.register(parentDisposable, () -> unregisterReferenceProvider(scope, provider)); + } myBindingCache.clear(); } public void unregisterReferenceProvider(@NotNull Class scope, @NotNull PsiReferenceProvider provider) { - myBindingsMap.get(scope).unregisterProvider(provider); + final SimpleProviderBinding binding = myBindingsMap.get(scope); + if (binding != null) { + binding.unregisterProvider(provider); + if (binding.isEmpty()) { + myBindingsMap.remove(scope); + } + } } - private void registerNamedReferenceProvider(@NotNull String[] names, final PsiNamePatternCondition nameCondition, @NotNull Class scopeClass, final boolean caseSensitive, @NotNull PsiReferenceProvider provider, final double priority, - @NotNull ElementPattern pattern) { + @NotNull ElementPattern pattern, + @Nullable Disposable parentDisposable) { NamedObjectProviderBinding providerBinding = myNamedBindingsMap.get(scopeClass); if (providerBinding == null) { @@ -116,6 +135,15 @@ public class PsiReferenceRegistrarImpl extends PsiReferenceRegistrar { }); } providerBinding.registerProvider(names, pattern, caseSensitive, provider, priority); + if (parentDisposable != null) { + NamedObjectProviderBinding finalProviderBinding = providerBinding; + Disposer.register(parentDisposable, () -> { + finalProviderBinding.unregisterProvider(provider); + if (finalProviderBinding.isEmpty()) { + myNamedBindingsMap.remove(scopeClass); + } + }); + } } @NotNull diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistryImpl.java b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistryImpl.java index dd09e2c92a5a..af5b7e8edf04 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistryImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistryImpl.java @@ -1,28 +1,20 @@ -/* - * Copyright 2000-2014 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. - */ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.psi.impl.source.resolve.reference; import com.intellij.lang.Language; import com.intellij.lang.LanguageExtension; +import com.intellij.lang.LanguageUtil; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.extensions.ExtensionPointListener; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.project.IndexNotReadyException; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.registry.Registry; import com.intellij.patterns.ElementPattern; import com.intellij.psi.*; +import com.intellij.util.KeyedLazyInstance; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; @@ -33,17 +25,49 @@ import java.util.*; public class ReferenceProvidersRegistryImpl extends ReferenceProvidersRegistry { private static final LanguageExtension CONTRIBUTOR_EXTENSION = - new LanguageExtension<>(PsiReferenceContributor.EP_NAME.getName()); + new LanguageExtension<>(PsiReferenceContributor.EP_NAME); private static final LanguageExtension REFERENCE_PROVIDER_EXTENSION = new LanguageExtension<>(PsiReferenceProviderBean.EP_NAME.getName()); private final Map myRegistrars = ContainerUtil.newConcurrentMap(); + public ReferenceProvidersRegistryImpl() { + if (Extensions.getRootArea().hasExtensionPoint(PsiReferenceContributor.EP_NAME)) { + PsiReferenceContributor.EP_NAME.addExtensionPointListener(new ExtensionPointListener>() { + @Override + public void extensionAdded(@NotNull KeyedLazyInstance extension, + @NotNull PluginDescriptor pluginDescriptor) { + Language language = Language.findLanguageByID(extension.getKey()); + if (language == Language.ANY) { + for (PsiReferenceRegistrarImpl registrar : myRegistrars.values()) { + registerContributedReferenceProviders(registrar, extension.getInstance()); + } + } + else if (language != null) { + Set languageAndDialects = LanguageUtil.getAllDerivedLanguages(language); + for (Language languageOrDialect : languageAndDialects) { + final PsiReferenceRegistrarImpl registrar = myRegistrars.get(languageOrDialect); + if (registrar != null) { + registerContributedReferenceProviders(registrar, extension.getInstance()); + } + } + } + } + + @Override + public void extensionRemoved(@NotNull KeyedLazyInstance extension, + @NotNull PluginDescriptor pluginDescriptor) { + Disposer.dispose(extension.getInstance()); + } + }, ApplicationManager.getApplication()); + } + } + @NotNull private static PsiReferenceRegistrarImpl createRegistrar(Language language) { PsiReferenceRegistrarImpl registrar = new PsiReferenceRegistrarImpl(); for (PsiReferenceContributor contributor : CONTRIBUTOR_EXTENSION.allForLanguageOrAny(language)) { - contributor.registerReferenceProviders(registrar); + registerContributedReferenceProviders(registrar, contributor); } List referenceProviderBeans = REFERENCE_PROVIDER_EXTENSION.allForLanguageOrAny(language); @@ -75,6 +99,10 @@ public class ReferenceProvidersRegistryImpl extends ReferenceProvidersRegistry { return registrar; } + private static void registerContributedReferenceProviders(PsiReferenceRegistrarImpl registrar, PsiReferenceContributor contributor) { + contributor.registerReferenceProviders(new TrackingReferenceRegistrar(registrar, contributor)); + Disposer.register(ApplicationManager.getApplication(), contributor); + } @NotNull @Override @@ -92,6 +120,11 @@ public class ReferenceProvidersRegistryImpl extends ReferenceProvidersRegistry { return registrar; } + @Override + public void unloadRegistrar(@NotNull Language language) { + myRegistrars.remove(language); + } + @NotNull @Override // 1. we create priorities map: "priority" -> non-empty references from providers diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/SimpleProviderBinding.java b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/SimpleProviderBinding.java index cdb25ef5d756..115261cd95a5 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/SimpleProviderBinding.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/SimpleProviderBinding.java @@ -49,4 +49,8 @@ class SimpleProviderBinding implements ProviderBinding { } } } + + public boolean isEmpty() { + return myProviderPairs.isEmpty(); + } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/TrackingReferenceRegistrar.java b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/TrackingReferenceRegistrar.java new file mode 100644 index 000000000000..03b23c46d093 --- /dev/null +++ b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/TrackingReferenceRegistrar.java @@ -0,0 +1,26 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.psi.impl.source.resolve.reference; + +import com.intellij.openapi.Disposable; +import com.intellij.patterns.ElementPattern; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReferenceProvider; +import com.intellij.psi.PsiReferenceRegistrar; +import org.jetbrains.annotations.NotNull; + +public class TrackingReferenceRegistrar extends PsiReferenceRegistrar { + private final PsiReferenceRegistrarImpl myDelegate; + private final Disposable myDisposable; + + public TrackingReferenceRegistrar(PsiReferenceRegistrarImpl delegate, Disposable disposable) { + myDelegate = delegate; + myDisposable = disposable; + } + + @Override + public void registerReferenceProvider(@NotNull ElementPattern pattern, + @NotNull PsiReferenceProvider provider, + double priority) { + myDelegate.registerReferenceProvider(pattern, provider, priority, myDisposable); + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java index a7640f44992b..a3f282834680 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java @@ -50,7 +50,6 @@ import com.intellij.openapi.vfs.newvfs.FileSystemInterface; import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.openapi.vfs.newvfs.impl.StubVirtualFile; -import com.intellij.psi.tree.IElementType; import com.intellij.testFramework.LightVirtualFile; import com.intellij.ui.GuiUtils; import com.intellij.util.*; @@ -301,7 +300,6 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent if (fileType instanceof LanguageFileType) { final LanguageFileType languageFileType = (LanguageFileType)fileType; if (!languageFileType.isSecondary()) { - IElementType.unregisterElementTypes(languageFileType.getLanguage()); Language.unregisterLanguage(languageFileType.getLanguage()); } } diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index 194ce8ae10c1..f247d3a6e5e8 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -538,7 +538,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -672,7 +672,7 @@ - +