Unloadable PsiReferenceContributor

GitOrigin-RevId: 762a3d5a70c32b3dcba601167ef55195fc2ef8ad
This commit is contained in:
Dmitry Jemerov
2019-10-11 12:04:37 +00:00
committed by intellij-monorepo-bot
parent 5628961271
commit 098d0ef15d
10 changed files with 130 additions and 44 deletions
@@ -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()) {
@@ -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<PsiReferenceContributor> EP_NAME = ExtensionPointName.create("com.intellij.psi.referenceContributor");
public static final ExtensionPointName<KeyedLazyInstance<PsiReferenceContributor>> EP_NAME = ExtensionPointName.create("com.intellij.psi.referenceContributor");
public abstract void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar);
@@ -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);
}
@@ -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);
@@ -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 <T extends PsiElement> void registerReferenceProvider(@NotNull ElementPattern<T> pattern,
@NotNull PsiReferenceProvider provider,
double priority) {
if (myInitialized && !ApplicationManager.getApplication().isUnitTestMode()) {
registerReferenceProvider(pattern, provider, priority, null);
}
public <T extends PsiElement> void registerReferenceProvider(@NotNull ElementPattern<T> 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<? super String> condition1 : conditions1) {
if (condition1 instanceof ValuePatternCondition) {
final Collection<String> 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
@@ -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<PsiReferenceContributor> CONTRIBUTOR_EXTENSION =
new LanguageExtension<>(PsiReferenceContributor.EP_NAME.getName());
new LanguageExtension<>(PsiReferenceContributor.EP_NAME);
private static final LanguageExtension<PsiReferenceProviderBean> REFERENCE_PROVIDER_EXTENSION =
new LanguageExtension<>(PsiReferenceProviderBean.EP_NAME.getName());
private final Map<Language, PsiReferenceRegistrarImpl> myRegistrars = ContainerUtil.newConcurrentMap();
public ReferenceProvidersRegistryImpl() {
if (Extensions.getRootArea().hasExtensionPoint(PsiReferenceContributor.EP_NAME)) {
PsiReferenceContributor.EP_NAME.addExtensionPointListener(new ExtensionPointListener<KeyedLazyInstance<PsiReferenceContributor>>() {
@Override
public void extensionAdded(@NotNull KeyedLazyInstance<PsiReferenceContributor> 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<Language> 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<PsiReferenceContributor> 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<PsiReferenceProviderBean> 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
@@ -49,4 +49,8 @@ class SimpleProviderBinding implements ProviderBinding {
}
}
}
public boolean isEmpty() {
return myProviderPairs.isEmpty();
}
}
@@ -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 <T extends PsiElement> void registerReferenceProvider(@NotNull ElementPattern<T> pattern,
@NotNull PsiReferenceProvider provider,
double priority) {
myDelegate.registerReferenceProvider(pattern, provider, priority, myDisposable);
}
}
@@ -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());
}
}
@@ -538,7 +538,7 @@
<extensionPoint name="psi.fileReferenceHelper"
interface="com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceHelper"/>
<extensionPoint name="psi.referenceContributor" beanClass="com.intellij.psi.impl.source.resolve.reference.PsiReferenceContributorEP">
<extensionPoint name="psi.referenceContributor" beanClass="com.intellij.psi.impl.source.resolve.reference.PsiReferenceContributorEP" dynamic="true">
<with attribute="implementation" implements="com.intellij.psi.PsiReferenceContributor"/>
</extensionPoint>
@@ -642,7 +642,7 @@
<extensionPoint name="navbar" interface="com.intellij.ide.navigationToolbar.NavBarModelExtension"/>
<extensionPoint name="renameHandler" interface="com.intellij.refactoring.rename.RenameHandler"/>
<extensionPoint name="renamePsiElementProcessor" interface="com.intellij.refactoring.rename.RenamePsiElementProcessor"/>
<extensionPoint name="renamePsiElementProcessor" interface="com.intellij.refactoring.rename.RenamePsiElementProcessor" dynamic="true"/>
<extensionPoint name="updateAddedFileProcessor" interface="com.intellij.psi.impl.file.UpdateAddedFileProcessor"/>
<extensionPoint name="moveFileHandler" interface="com.intellij.refactoring.move.moveFilesOrDirectories.MoveFileHandler"/>
<extensionPoint name="automaticRenamerFactory" interface="com.intellij.refactoring.rename.naming.AutomaticRenamerFactory"/>
@@ -672,7 +672,7 @@
<extensionPoint name="colorAndFontPanelFactory" interface="com.intellij.application.options.colors.ColorAndFontPanelFactory"/>
<extensionPoint name="colorAndFontDescriptorProvider" interface="com.intellij.openapi.options.colors.ColorAndFontDescriptorsProvider"/>
<extensionPoint name="codeInsight.parameterInfo" beanClass="com.intellij.lang.LanguageExtensionPoint">
<extensionPoint name="codeInsight.parameterInfo" beanClass="com.intellij.lang.LanguageExtensionPoint" dynamic="true">
<with attribute="implementationClass" implements="com.intellij.lang.parameterInfo.ParameterInfoHandler"/>
</extensionPoint>
<extensionPoint name="codeInsight.parameterInfo.listener" interface="com.intellij.codeInsight.hint.ParameterInfoListener" />