diff --git a/platform/core-api/src/com/intellij/model/psi/PsiExternalReferenceHost.java b/platform/core-api/src/com/intellij/model/psi/PsiExternalReferenceHost.java new file mode 100644 index 000000000000..d5ec6a868bfc --- /dev/null +++ b/platform/core-api/src/com/intellij/model/psi/PsiExternalReferenceHost.java @@ -0,0 +1,20 @@ +// Copyright 2000-2020 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.model.psi; + +import com.intellij.psi.PsiElement; + +/** + * Elements which support external references should implement this interface. + *

+ * There are two kinds of element references: own references and external references.
+ * Own references are references the element knows about, they are usually used by language support. + * Element doesn't know about external references since, for example, they might be contributed by plugins. + * External references are used for navigation/Find Usages/etc as well as own references. + *

+ * The element must implement this interface to support hosting external references, + * so this mechanism is effectively opt-in. + * + * @see PsiElement#getOwnReferences + */ +public interface PsiExternalReferenceHost extends PsiElement { +} diff --git a/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceHints.java b/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceHints.java new file mode 100644 index 000000000000..299d84f8da5f --- /dev/null +++ b/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceHints.java @@ -0,0 +1,52 @@ +// Copyright 2000-2020 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.model.psi; + +import com.intellij.model.Symbol; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface PsiSymbolReferenceHints { + + /** + * Provider may return only references which could be resolved to symbols of this type if the type is not {@code null}. + * + * @return type of expected target symbol + */ + @Nullable + default Class getTargetClass() { + Symbol target = getTarget(); + return target != null ? target.getClass() : null; + } + + /** + * Provider may return only references which could be resolved to specified symbol if the symbol is not {@code null}. + * + * @return expected target symbol + */ + @Nullable + default Symbol getTarget() { + return null; + } + + /** + * Provider may return references which contain specified offset if the offset is greater than or equal to 0; + * in this case the offset is guaranteed to be within {@code [0, element.getTextLength())}. + * + * @return offset in the element for which references are queried, or {@code null} if the offset doesn't matter + */ + @Nullable + default Integer getOffsetInElement() { + return null; + } + + @NotNull + static PsiSymbolReferenceHints offsetHint(int offsetInElement) { + assert offsetInElement >= 0; + return new PsiSymbolReferenceHints() { + @Override + public Integer getOffsetInElement() { + return offsetInElement; + } + }; + } +} diff --git a/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceProvider.java b/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceProvider.java new file mode 100644 index 000000000000..45495579398f --- /dev/null +++ b/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceProvider.java @@ -0,0 +1,12 @@ +// Copyright 2000-2020 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.model.psi; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +public interface PsiSymbolReferenceProvider { + + @NotNull + Collection getReferences(@NotNull PsiExternalReferenceHost element, @NotNull PsiSymbolReferenceHints hints); +} diff --git a/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceProviderBean.java b/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceProviderBean.java new file mode 100644 index 000000000000..38b3f6c54312 --- /dev/null +++ b/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceProviderBean.java @@ -0,0 +1,78 @@ +// Copyright 2000-2020 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.model.psi; + +import com.intellij.diagnostic.PluginException; +import com.intellij.lang.Language; +import com.intellij.model.Symbol; +import com.intellij.openapi.extensions.CustomLoadingExtensionPointBean; +import com.intellij.openapi.extensions.PluginDescriptor; +import com.intellij.openapi.extensions.RequiredElement; +import com.intellij.util.xmlb.annotations.Attribute; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PsiSymbolReferenceProviderBean extends CustomLoadingExtensionPointBean { + + /** + * {@link Language#getID() id} of the language for which references are provided.
+ * The references will be provided for the specified language and its {@linkplain Language#getBaseLanguage() base languages}. + */ + @Attribute + @RequiredElement + public String hostLanguage; + + /** + * Fully qualified name of the class of the PsiElement for which references are provided.
+ * The references will be provided for the specified class and its superclasses. + */ + @Attribute + @RequiredElement + public String hostElementClass; + + /** + * Fully qualified name of the common supertype of all symbols that this provider's references could resolve to. + */ + @Attribute + @RequiredElement + public String targetClass; + + @Attribute + @RequiredElement + public String implementationClass; + + @Nullable + @Override + protected String getImplementationClassName() { + return implementationClass; + } + + @NotNull + public Language getHostLanguage() { + Language language = Language.findLanguageByID(hostLanguage); + if (language == null) { + throw new PluginException("Cannot find language '" + hostLanguage + "'", getPluginDescriptor().getPluginId()); + } + return language; + } + + @NotNull + public Class getHostElementClass() { + return loadClass(hostElementClass); + } + + @NotNull + public Class getResolveTargetClass() { + return loadClass(targetClass); + } + + @SuppressWarnings("unchecked") + private Class loadClass(@NotNull String fqn) { + PluginDescriptor pluginDescriptor = getPluginDescriptor(); + try { + return (Class)Class.forName(fqn, true, pluginDescriptor.getPluginClassLoader()); + } + catch (ClassNotFoundException e) { + throw new PluginException(e, pluginDescriptor.getPluginId()); + } + } +} diff --git a/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceService.java b/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceService.java new file mode 100644 index 000000000000..73c4a1a8ad53 --- /dev/null +++ b/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceService.java @@ -0,0 +1,29 @@ +// Copyright 2000-2020 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.model.psi; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + +/** + * Entry point for obtaining {@link PsiSymbolReference}s from {@link PsiElement}. + */ +public interface PsiSymbolReferenceService { + + @NotNull + static PsiSymbolReferenceService getService() { + return ServiceManager.getService(PsiSymbolReferenceService.class); + } + + /** + * @return all (own and external) references from this element + */ + @NotNull + Iterable getReferences(@NotNull PsiElement element); + + /** + * @return all (own and external) references from this element, which match {@code hints} + */ + @NotNull + Iterable getReferences(@NotNull PsiElement element, @NotNull PsiSymbolReferenceHints hints); +} diff --git a/platform/core-api/src/com/intellij/psi/PsiElement.java b/platform/core-api/src/com/intellij/psi/PsiElement.java index 6fe6177844f3..87a6ee3e0ad2 100644 --- a/platform/core-api/src/com/intellij/psi/PsiElement.java +++ b/platform/core-api/src/com/intellij/psi/PsiElement.java @@ -1,8 +1,9 @@ -// Copyright 2000-2018 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. +// Copyright 2000-2020 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; import com.intellij.lang.ASTNode; import com.intellij.lang.Language; +import com.intellij.model.psi.PsiSymbolReference; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.Key; @@ -13,11 +14,14 @@ import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.SearchScope; import com.intellij.util.ArrayFactory; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.ApiStatus.Experimental; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; + /** * The common base interface for all elements of the PSI tree. *

@@ -429,6 +433,26 @@ public interface PsiElement extends UserDataHolder, Iconable { @Contract(pure=true) boolean isWritable(); + /** + * The returned references are expected to be used by language support, + * for example in Java `foo` element in `foo = 42` expression has a reference, + * which is used by Java language support to compute expected type of the assignment. + *

+ * On the other hand {@code "bar"} literal in {@code new File("bar")} is a string literal, + * and from Java language perspective it has no references, + * but the framework support "knows" that this literal contains the reference to a file. + * These are external references. + * + * @return collection of references from this element, or empty collection if there are no such references + * @see com.intellij.model.psi.PsiExternalReferenceHost + * @see com.intellij.model.psi.PsiSymbolReferenceService#getReferences(PsiElement) + */ + @Experimental + @NotNull + default Iterable getOwnReferences() { + return Arrays.asList(getReferences()); + } + /** * Returns the reference from this PSI element to another PSI element (or elements), if one exists. * If the element has multiple associated references (see {@link #getReferences()} diff --git a/platform/core-impl/resources/META-INF/CoreImpl.xml b/platform/core-impl/resources/META-INF/CoreImpl.xml index b45233eb52dd..24a1e2bc92c5 100644 --- a/platform/core-impl/resources/META-INF/CoreImpl.xml +++ b/platform/core-impl/resources/META-INF/CoreImpl.xml @@ -11,6 +11,13 @@ + + + + +