getPySources(TargetIdeInfo target) {
return ImmutableList.of();
}
- /** Maps a blaze artifact to the import string used to reference it. */
+ /**
+ * Maps a blaze artifact to the import string used to reference it.
+ */
@Nullable
abstract QualifiedName toImportString(ArtifactLocation source);
@@ -156,4 +169,139 @@ static QualifiedName fromRelativePath(String relativePath) {
relativePath = StringUtil.trimExtensions(relativePath);
return QualifiedName.fromComponents(StringUtil.split(relativePath, File.separator));
}
+
+ /**
+ *
+ * Introspects the target and extracts any imports as {@link QualifiedName} objects. The imports
+ * are relative to the basedir of the `BUILD` file and so the logic will adjust the imports to be
+ * from that directory. Later the file-paths related to the Bazel target are converted to
+ * {@link QualifiedName}s as well. By looking for the imports' {@link QualifiedName} as prefix on
+ * the source files' fully formed {@link QualifiedName}s, it is possible to derive how the Python
+ * interpreter would experience the modules and therefore reflect this in the mapping from the
+ * module names to the sources.
+ *
+ *
+ * An example; Consider a `py_library` target `:mylib` that is defined in a
+ * BUILD.bazel
file;
+ *
+ *
+ * {@code
+ * a/
+ * b/
+ * c/
+ * BUILD.bazel
+ * d/
+ * e.py
+ * }
+ *
+ *
+ * The py_library
might have an imports
attribute of .
+ * and in consideration of the BUILD.bazel
path a/b/c/BUILD.bazel
, this
+ * means that the imports
{@link QualifiedName} will have components
+ * a,b,c
. The logic at {@link #buildSourcesIndex(Project, BlazeProjectData)} will
+ * consider file a/b/c/d/e.py
which will convert to a {@link QualifiedName} with
+ * components a,b,c,d,e
and by removing the prefix obtained from
+ * imports
, the final module {@link QualifiedName} will have components
+ * d,e
.
+ *
+ */
+ private static List assembleImportRoots(TargetIdeInfo target) {
+ ArtifactLocation buildFileArtifactLocation = target.getBuildFile();
+
+ if (null == buildFileArtifactLocation) {
+ return ImmutableList.of();
+ }
+
+ PyIdeInfo ideInfo = target.getPyIdeInfo();
+
+ if (null == ideInfo) {
+ return ImmutableList.of();
+ }
+
+ Path buildPath = Path.of(target.getBuildFile().getExecutionRootRelativePath());
+ Path buildParentPath = buildPath.getParent();
+
+ // In the case of an external repo the build path could be `/BUILD`
+ // which is problematic because the basedir is `/` which makes sense
+ // in the context of a Bazel repo but not in the context of the overall
+ // file-system. For this reason, the special case of `/BUILD` is taken
+ // to have a basedir of `.`.
+
+ if (null == buildParentPath || 0 == buildParentPath.getNameCount()) {
+ buildParentPath = PATH_CURRENT_DIR;
+ }
+
+ ImmutableList.Builder resultBuilder = ImmutableList.builder();
+
+ for (String imp : ideInfo.getImports()) {
+ Path impPath = buildParentPath.resolve(imp).normalize();
+ String[] impPathParts = new String[impPath.getNameCount()];
+
+ for (int i = impPath.getNameCount() - 1; i >= 0; i--) {
+ impPathParts[i] = impPath.getName(i).toString();
+ }
+
+ resultBuilder.add(QualifiedName.fromComponents(impPathParts));
+ }
+
+ return resultBuilder.build();
+ }
+
+ /**
+ * For each of the importRoots
, see if it matches as a prefix on the
+ * sourceImport
and then trim off the prefix; yielding the true module name.
+ * Also include the original in the list as well because this still seems to be able to be
+ * used as well. See {@link #assembleImportRoots(TargetIdeInfo)} for additional background.
+ */
+ private static List assembleSourceImportsFromImportRoots(
+ List importRoots,
+ QualifiedName sourceImport) {
+ if (null == sourceImport || null == sourceImport.getLastComponent()) {
+ return ImmutableList.of();
+ }
+
+ ImmutableList.Builder result = ImmutableList.builder();
+
+ result.add(sourceImport);
+
+ for (QualifiedName importsName : importRoots) {
+ // if the import name equals the name this is a strange situation.
+ if (!importsName.equals(sourceImport) && sourceImport.matchesPrefix(importsName)) {
+ result.add(sourceImport.subQualifiedName(importsName.getComponentCount(),
+ sourceImport.getComponentCount()));
+ }
+ }
+
+ return result.build();
+ }
+
+ /**
+ * This interface is used to isolate out the calls to statics so that unit testing is possible
+ * on this class.
+ */
+ interface ArtifactSupplierToPsiElementProviderMapper {
+
+ PsiElementProvider map(Project project, ArtifactLocationDecoder decoder,
+ ArtifactLocation source);
+ }
+
+ private static class DefaultArtifactSupplierToPsiElementProviderMapper implements
+ ArtifactSupplierToPsiElementProviderMapper {
+
+ @Override
+ public PsiElementProvider map(Project project, ArtifactLocationDecoder decoder,
+ ArtifactLocation source) {
+ return (manager) -> {
+ File file = OutputArtifactResolver.resolve(project, decoder, source);
+ if (file == null) {
+ return null;
+ }
+ if (PyNames.INIT_DOT_PY.equals(file.getName())) {
+ file = file.getParentFile();
+ }
+ return BlazePyResolverUtils.resolveFile(manager, file);
+ };
+ }
+ }
+
}
diff --git a/python/src/com/google/idea/blaze/python/resolve/provider/BazelPyImportResolverStrategy.java b/python/src/com/google/idea/blaze/python/resolve/provider/BazelPyImportResolverStrategy.java
index ed168e8ae21..70b5e75a10f 100644
--- a/python/src/com/google/idea/blaze/python/resolve/provider/BazelPyImportResolverStrategy.java
+++ b/python/src/com/google/idea/blaze/python/resolve/provider/BazelPyImportResolverStrategy.java
@@ -51,15 +51,6 @@ protected QualifiedName toImportString(ArtifactLocation source) {
if (source.isGenerated() || !source.getRelativePath().endsWith(".py")) {
return null;
}
- QualifiedName name = fromRelativePath(source.getRelativePath());
- // pip_parse and pip_install macros unzip pip wheels into site-packages dir
- // as result import strings get site-packages prefix, remove the prefix.
- // Reference to python rules where site-packages is added:
- // https://github.com/bazelbuild/rules_python/blob/834149dfcd9e0dcb9d713caeb6bf5b0584601392/python/pip_install/extract_wheels/wheel.py#L75
- if (name != null
- && name.toString().startsWith("site-packages.")) {
- name = name.removeHead(1);
- }
- return name;
+ return fromRelativePath(source.getRelativePath());
}
}
diff --git a/python/tests/unittests/com/google/idea/blaze/python/resolve/provider/AbstractPyImportResolverStrategyTest.java b/python/tests/unittests/com/google/idea/blaze/python/resolve/provider/AbstractPyImportResolverStrategyTest.java
new file mode 100644
index 00000000000..9654c506aaf
--- /dev/null
+++ b/python/tests/unittests/com/google/idea/blaze/python/resolve/provider/AbstractPyImportResolverStrategyTest.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2024 The Bazel Authors. All rights reserved.
+ *
+ * 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.google.idea.blaze.python.resolve.provider;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.idea.blaze.base.BlazeTestCase;
+import com.google.idea.blaze.base.ideinfo.*;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.settings.BuildSystemName;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
+import com.google.idea.blaze.base.sync.workspace.MockArtifactLocationDecoder;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.util.QualifiedName;
+import com.jetbrains.python.psi.resolve.PyQualifiedNameResolveContext;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.util.Set;
+
+import static com.google.common.truth.Truth.assertThat;
+
+public class AbstractPyImportResolverStrategyTest extends BlazeTestCase {
+
+ /**
+ * It is possible to specify import paths for a py_...
target so that the modules in
+ * the Python side can be specified. This test checks what happens in this situation.
+ */
+ @Test
+ public void testBuildSourcesIndexWithAnImportRoot() {
+ AbstractPyImportResolverStrategy strategy = new TestPyImportResolverStrategy();
+ TargetMap targetMap = assembleTestTargetMap(Set.of("river"));
+
+ Project project = Mockito.mock(Project.class);
+ BlazeProjectData projectData = Mockito.mock(BlazeProjectData.class);
+
+ Mockito.when(projectData.getTargetMap()).thenReturn(targetMap);
+ Mockito.when(projectData.getArtifactLocationDecoder()).thenReturn(
+ new MockArtifactLocationDecoder(new File("/workspaceroot"), false));
+
+ // code under test
+ PySourcesIndex actualSourcesIndex = strategy.buildSourcesIndex(project, projectData);
+
+ // Verify the short names capture the right mapping to possible modules.
+ Set shortNamesImports = actualSourcesIndex.shortNames.get("bar");
+ assertThat(shortNamesImports).containsExactly(
+ QualifiedName.fromComponents("whistle", "foo", "river", "lib", "bar"),
+ // ^ this one is the default which is the complete path in the Bazel repo to the Python file
+ QualifiedName.fromComponents("lib", "bar")
+ // ^ this one is created from the target's `imports`.
+ );
+
+ // Verify that the mappings from possible modules to actual files works.
+ // Because the strategy class was initialized with a special class to provide the PsiElement, we know that the
+ // `toString()` method will return specific information that we can relay on in this test.
+
+ assertThat(actualSourcesIndex.sourceMap).hasSize(4);
+ // ^ the mapping to the Python file, and also it's parent with the two possible paths
+ // one from the imports and one being the default.
+
+ PsiManager manager = Mockito.mock(PsiManager.class);
+
+ QualifiedName[] expectedBarModules = new QualifiedName[]{
+ QualifiedName.fromComponents("whistle", "foo", "river", "lib", "bar"),
+ QualifiedName.fromComponents("lib", "bar")
+ };
+
+ for (QualifiedName expectedBarModule : expectedBarModules) {
+ PsiElement fullElement = actualSourcesIndex.sourceMap.get(expectedBarModule).get(manager);
+ assertThat(fullElement).isNotNull();
+ assertThat(fullElement.toString()).isEqualTo("whistle/foo/river/lib/bar.py");
+ }
+
+ // the parent dir of the Python file is also included in the mapping.
+
+ QualifiedName[] expectedLibModules = new QualifiedName[]{
+ QualifiedName.fromComponents("whistle", "foo", "river", "lib"),
+ QualifiedName.fromComponents("lib"),
+ };
+
+ for (QualifiedName expectedLibModule : expectedLibModules) {
+ PsiElement fullElement = actualSourcesIndex.sourceMap.get(expectedLibModule).get(manager);
+ assertThat(fullElement).isNotNull();
+ assertThat(fullElement.toString()).isEqualTo("whistle/foo/river/lib");
+ }
+ }
+
+ /**
+ * This is the case where there is no import roots supplied. In this case, we expect that the only
+ * allowed Python module path will be the one from the root of the Bazel repo.
+ */
+ @Test
+ public void testBuildSourcesIndexWithNoImportRoot() {
+ AbstractPyImportResolverStrategy strategy = new TestPyImportResolverStrategy();
+ TargetMap targetMap = assembleTestTargetMap(Set.of()); // <-- note empty
+
+ Project project = Mockito.mock(Project.class);
+ BlazeProjectData projectData = Mockito.mock(BlazeProjectData.class);
+
+ Mockito.when(projectData.getTargetMap()).thenReturn(targetMap);
+ Mockito.when(projectData.getArtifactLocationDecoder()).thenReturn(
+ new MockArtifactLocationDecoder(new File("/workspaceroot"), false));
+
+ // code under test
+ PySourcesIndex actualSourcesIndex = strategy.buildSourcesIndex(project, projectData);
+
+ // Verify the short names capture the right mapping to possible modules.
+ Set shortNamesImports = actualSourcesIndex.shortNames.get("bar");
+ assertThat(shortNamesImports).containsExactly(
+ QualifiedName.fromComponents("whistle", "foo", "river", "lib", "bar")
+ // ^ this one is the default which is the complete path in the Bazel repo to the Python file
+ );
+
+ // Verify that the mappings from possible modules to actual files works.
+ // Because the strategy class was initialized with a special class to provide the PsiElement, we know that the
+ // `toString()` method will return specific information that we can relay on in this test.
+
+ assertThat(actualSourcesIndex.sourceMap).hasSize(2);
+ // ^ the mapping to the Python file, and also it's parent.
+
+ PsiManager manager = Mockito.mock(PsiManager.class);
+
+ QualifiedName[] expectedBarModules = new QualifiedName[]{
+ QualifiedName.fromComponents("whistle", "foo", "river", "lib", "bar"),
+ };
+
+ for (QualifiedName expectedBarModule : expectedBarModules) {
+ PsiElement fullElement = actualSourcesIndex.sourceMap.get(expectedBarModule).get(manager);
+ assertThat(fullElement).isNotNull();
+ assertThat(fullElement.toString()).isEqualTo("whistle/foo/river/lib/bar.py");
+ }
+
+ // Check that the parent dir of the Python file is also included in the mapping.
+
+ QualifiedName[] expectedLibModules = new QualifiedName[]{
+ QualifiedName.fromComponents("whistle", "foo", "river", "lib"),
+ };
+
+ for (QualifiedName expectedLibModule : expectedLibModules) {
+ PsiElement fullElement = actualSourcesIndex.sourceMap.get(expectedLibModule).get(manager);
+ assertThat(fullElement).isNotNull();
+ assertThat(fullElement.toString()).isEqualTo("whistle/foo/river/lib");
+ }
+ }
+
+ /**
+ * This is similar to {@link #testBuildSourcesIndexWithAnImportRoot()} but covers the case where
+ * the import path is `.` to check that this will root the import statements from the top of the
+ * Bazel project.
+ */
+ @Test
+ public void testBuildSourcesIndexWithDotImportRoot() {
+ AbstractPyImportResolverStrategy strategy = new TestPyImportResolverStrategy();
+ TargetMap targetMap = assembleTestTargetMap(Set.of("."));
+
+ Project project = Mockito.mock(Project.class);
+ BlazeProjectData projectData = Mockito.mock(BlazeProjectData.class);
+
+ Mockito.when(projectData.getTargetMap()).thenReturn(targetMap);
+ Mockito.when(projectData.getArtifactLocationDecoder()).thenReturn(
+ new MockArtifactLocationDecoder(new File("/workspaceroot"), false));
+
+ // code under test
+ PySourcesIndex actualSourcesIndex = strategy.buildSourcesIndex(project, projectData);
+
+ // Verify the short names capture the right mapping to possible modules.
+ Set shortNamesImports = actualSourcesIndex.shortNames.get("bar");
+ assertThat(shortNamesImports).containsExactly(
+ QualifiedName.fromComponents("whistle", "foo", "river", "lib", "bar"),
+ // ^ this one is the default which is the complete path in the Bazel repo to the Python file
+ QualifiedName.fromComponents("river", "lib", "bar")
+ // ^ this one is created from the target's `imports`.
+ );
+
+ // Verify that the mappings from possible modules to actual files works.
+ // Because the strategy class was initialized with a special class to provide the PsiElement, we know that the
+ // `toString()` method will return specific information that we can relay on in this test.
+
+ // Checks for the imports to the directory which is parent to the Python file have
+ // been done in other tests and won't be repeated here.
+
+ PsiManager manager = Mockito.mock(PsiManager.class);
+
+ QualifiedName[] expectedBarModules = new QualifiedName[]{
+ QualifiedName.fromComponents("whistle", "foo", "river", "lib", "bar"),
+ QualifiedName.fromComponents("river", "lib", "bar")
+ };
+
+ for (QualifiedName expectedBarModule : expectedBarModules) {
+ PsiElement fullElement = actualSourcesIndex.sourceMap.get(expectedBarModule).get(manager);
+ assertThat(fullElement).isNotNull();
+ assertThat(fullElement.toString()).isEqualTo("whistle/foo/river/lib/bar.py");
+ }
+ }
+
+ /**
+ * This case covers a special situation where the `BUILD.bazel` file is at the top of the repo.
+ * This happens for a Python wheel and because the `BUILD.bazel` file is `/` we have to be careful
+ * that we don't make reference to the top of the file system.
+ */
+ @Test
+ public void testBuildSourcesIndexWithBuildAtRootAndImport() {
+ AbstractPyImportResolverStrategy strategy = new TestPyImportResolverStrategy();
+
+ PyIdeInfo.Builder pyIdeInfoBuilder = PyIdeInfo.builder()
+ .addSources(ImmutableSet.of(source("top_level/lib/tea.py")))
+ .addImports(ImmutableSet.copyOf(Set.of("top_level")));
+
+ TargetMap targetMap = TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel("//:tea")
+ .setBuildFile(source("/BUILD.bazel")) // <- note
+ .setPyInfo(pyIdeInfoBuilder)
+ )
+ .build();
+
+ Project project = Mockito.mock(Project.class);
+ BlazeProjectData projectData = Mockito.mock(BlazeProjectData.class);
+
+ Mockito.when(projectData.getTargetMap()).thenReturn(targetMap);
+ Mockito.when(projectData.getArtifactLocationDecoder()).thenReturn(
+ new MockArtifactLocationDecoder(new File("/workspaceroot"), false));
+
+ // code under test
+ PySourcesIndex actualSourcesIndex = strategy.buildSourcesIndex(project, projectData);
+
+ // Verify the short names capture the right mapping to possible modules.
+ Set shortNamesImports = actualSourcesIndex.shortNames.get("tea");
+ assertThat(shortNamesImports).containsExactly(
+ QualifiedName.fromComponents("top_level", "lib", "tea"),
+ // ^ this one is the default which is the complete path in the Bazel repo to the Python file
+ QualifiedName.fromComponents("lib", "tea")
+ // ^ this one is created from the target's `imports`.
+ );
+
+ // Verify that the mappings from possible modules to actual files works.
+ // Because the strategy class was initialized with a special class to provide the PsiElement, we know that the
+ // `toString()` method will return specific information that we can relay on in this test.
+
+ // Checks for the imports to the directory which is parent to the Python file have
+ // been done in other tests and won't be repeated here.
+
+ PsiManager manager = Mockito.mock(PsiManager.class);
+
+ QualifiedName[] expectedBarModules = new QualifiedName[]{
+ QualifiedName.fromComponents("top_level", "lib", "tea"),
+ QualifiedName.fromComponents("lib", "tea")
+ };
+
+ for (QualifiedName expectedBarModule : expectedBarModules) {
+ PsiElement fullElement = actualSourcesIndex.sourceMap.get(expectedBarModule).get(manager);
+ assertThat(fullElement).isNotNull();
+ assertThat(fullElement.toString()).isEqualTo("top_level/lib/tea.py");
+ }
+ }
+
+ private TargetMap assembleTestTargetMap(Set importPaths) {
+ PyIdeInfo.Builder pyIdeInfoBuilder = PyIdeInfo.builder()
+ .addSources(ImmutableSet.of(source("whistle/foo/river/lib/bar.py")))
+ .addImports(ImmutableSet.copyOf(importPaths));
+
+ return TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel("//whistle/foo:foo")
+ .setBuildFile(source("whistle/foo/BUILD.bazel"))
+ .setPyInfo(pyIdeInfoBuilder)
+ )
+ .build();
+ }
+
+ private static ArtifactLocation source(String relativePath) {
+ return ArtifactLocation.builder().setRelativePath(relativePath).setIsSource(true).build();
+ }
+
+ private static class MockArtifactSupplierToPsiElementProviderMapper
+ implements AbstractPyImportResolverStrategy.ArtifactSupplierToPsiElementProviderMapper {
+
+ @Override
+ public PsiElementProvider map(
+ Project project, ArtifactLocationDecoder decoder, ArtifactLocation source) {
+ return (manager) -> new MockArtifactLocationPsiElement(source.getExecutionRootRelativePath());
+ }
+ }
+
+ /**
+ * This is a concrete implementation of {@link AbstractPyImportResolverStrategy} so there is
+ * something to test.
+ */
+ private static class TestPyImportResolverStrategy extends AbstractPyImportResolverStrategy {
+
+ /**
+ * This constructor will install a fake mapper to {@link PsiElementProvider} so that the results
+ * can be captured to assert on.
+ */
+ public TestPyImportResolverStrategy() {
+ super(new MockArtifactSupplierToPsiElementProviderMapper());
+ }
+
+ @Nullable
+ @Override
+ QualifiedName toImportString(ArtifactLocation source) {
+ return fromRelativePath(source.getRelativePath());
+ }
+
+ @Nullable
+ @Override
+ public PsiElement resolveToWorkspaceSource(QualifiedName name,
+ PyQualifiedNameResolveContext context) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean appliesToBuildSystem(BuildSystemName buildSystemName) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+}
diff --git a/python/tests/unittests/com/google/idea/blaze/python/resolve/provider/MockArtifactLocationPsiElement.java b/python/tests/unittests/com/google/idea/blaze/python/resolve/provider/MockArtifactLocationPsiElement.java
new file mode 100644
index 00000000000..2762705d0d5
--- /dev/null
+++ b/python/tests/unittests/com/google/idea/blaze/python/resolve/provider/MockArtifactLocationPsiElement.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2024 The Bazel Authors. All rights reserved.
+ *
+ * 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.google.idea.blaze.python.resolve.provider;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.lang.Language;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.NlsSafe;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.*;
+import com.intellij.psi.scope.PsiScopeProcessor;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.SearchScope;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.io.File;
+
+/**
+ * This is a fake {@link PsiElement} that is mostly dummy methods; just covering enough to support
+ * the adjacent unit tests.
+ */
+
+public class MockArtifactLocationPsiElement implements PsiElement {
+
+ private final File executionRelativeFile;
+
+ public MockArtifactLocationPsiElement(String executationRelativePath) {
+ this.executionRelativeFile = new File(executationRelativePath);
+ }
+
+ @Override
+ public @NotNull Project getProject() throws PsiInvalidElementAccessException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @NotNull Language getLanguage() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiManager getManager() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @NotNull PsiElement[] getChildren() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement getParent() {
+ return new MockArtifactLocationPsiElement(executionRelativeFile.getParent());
+ }
+
+ @Override
+ public PsiElement getFirstChild() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement getLastChild() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement getNextSibling() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement getPrevSibling() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiFile getContainingFile() throws PsiInvalidElementAccessException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public TextRange getTextRange() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getStartOffsetInParent() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getTextLength() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @Nullable PsiElement findElementAt(int i) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @Nullable PsiReference findReferenceAt(int i) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getTextOffset() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @NlsSafe String getText() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public char[] textToCharArray() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement getNavigationElement() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement getOriginalElement() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean textMatches(@NotNull @NonNls CharSequence charSequence) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean textMatches(@NotNull PsiElement psiElement) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean textContains(char c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void accept(@NotNull PsiElementVisitor psiElementVisitor) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void acceptChildren(@NotNull PsiElementVisitor psiElementVisitor) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement copy() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement add(@NotNull PsiElement psiElement) throws IncorrectOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement addBefore(@NotNull PsiElement psiElement, @Nullable PsiElement psiElement1)
+ throws IncorrectOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement addAfter(@NotNull PsiElement psiElement, @Nullable PsiElement psiElement1)
+ throws IncorrectOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void checkAdd(@NotNull PsiElement psiElement) throws IncorrectOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement addRange(PsiElement psiElement, PsiElement psiElement1)
+ throws IncorrectOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement addRangeBefore(@NotNull PsiElement psiElement, @NotNull PsiElement psiElement1,
+ PsiElement psiElement2) throws IncorrectOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement addRangeAfter(PsiElement psiElement, PsiElement psiElement1,
+ PsiElement psiElement2) throws IncorrectOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void delete() throws IncorrectOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void checkDelete() throws IncorrectOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void deleteChildRange(PsiElement psiElement, PsiElement psiElement1)
+ throws IncorrectOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiElement replace(@NotNull PsiElement psiElement) throws IncorrectOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isValid() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isWritable() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @Nullable PsiReference getReference() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PsiReference[] getReferences() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @Nullable T getCopyableUserData(@NotNull Key key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void putCopyableUserData(@NotNull Key key, @Nullable T t) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean processDeclarations(@NotNull PsiScopeProcessor psiScopeProcessor,
+ @NotNull ResolveState resolveState, @Nullable PsiElement psiElement,
+ @NotNull PsiElement psiElement1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @Nullable PsiElement getContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isPhysical() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @NotNull GlobalSearchScope getResolveScope() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @NotNull SearchScope getUseScope() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ASTNode getNode() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isEquivalentTo(PsiElement psiElement) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Icon getIcon(int i) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @Nullable T getUserData(@NotNull Key key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void putUserData(@NotNull Key key, @Nullable T t) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toString() {
+ return executionRelativeFile.getPath();
+ }
+}
diff --git a/sdkcompat/v221/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java b/sdkcompat/v221/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
deleted file mode 100644
index 27f1689be89..00000000000
--- a/sdkcompat/v221/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.google.idea.sdkcompat.clion;
-
-import com.intellij.openapi.extensions.ExtensionPoint;
-import com.intellij.openapi.fileEditor.FileEditor;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.cidr.cpp.cmake.workspace.CMakeNotificationProvider;
-
-import javax.swing.*;
-
-// #api223
-public class CMakeNotificationProviderWrapper {
- CMakeNotificationProvider value;
-
- public CMakeNotificationProviderWrapper() {
- value = new CMakeNotificationProvider();
- }
-
- public JComponent createNotificationPanel(VirtualFile virtualFile, FileEditor fileEditor, Project project) {
- return value.createNotificationPanel(virtualFile, fileEditor, project);
- }
-
- public static void unregisterDelegateExtension(ExtensionPoint extensionPoint) {
- for (T extension : extensionPoint.getExtensions()) {
- if (extension instanceof CMakeNotificationProvider) {
- extensionPoint.unregisterExtension(extension);
- }
- }
- }
-}
diff --git a/sdkcompat/v222/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java b/sdkcompat/v222/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
deleted file mode 100644
index 27f1689be89..00000000000
--- a/sdkcompat/v222/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.google.idea.sdkcompat.clion;
-
-import com.intellij.openapi.extensions.ExtensionPoint;
-import com.intellij.openapi.fileEditor.FileEditor;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.cidr.cpp.cmake.workspace.CMakeNotificationProvider;
-
-import javax.swing.*;
-
-// #api223
-public class CMakeNotificationProviderWrapper {
- CMakeNotificationProvider value;
-
- public CMakeNotificationProviderWrapper() {
- value = new CMakeNotificationProvider();
- }
-
- public JComponent createNotificationPanel(VirtualFile virtualFile, FileEditor fileEditor, Project project) {
- return value.createNotificationPanel(virtualFile, fileEditor, project);
- }
-
- public static void unregisterDelegateExtension(ExtensionPoint extensionPoint) {
- for (T extension : extensionPoint.getExtensions()) {
- if (extension instanceof CMakeNotificationProvider) {
- extensionPoint.unregisterExtension(extension);
- }
- }
- }
-}
diff --git a/sdkcompat/v223/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java b/sdkcompat/v223/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
deleted file mode 100644
index 27f1689be89..00000000000
--- a/sdkcompat/v223/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.google.idea.sdkcompat.clion;
-
-import com.intellij.openapi.extensions.ExtensionPoint;
-import com.intellij.openapi.fileEditor.FileEditor;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.cidr.cpp.cmake.workspace.CMakeNotificationProvider;
-
-import javax.swing.*;
-
-// #api223
-public class CMakeNotificationProviderWrapper {
- CMakeNotificationProvider value;
-
- public CMakeNotificationProviderWrapper() {
- value = new CMakeNotificationProvider();
- }
-
- public JComponent createNotificationPanel(VirtualFile virtualFile, FileEditor fileEditor, Project project) {
- return value.createNotificationPanel(virtualFile, fileEditor, project);
- }
-
- public static void unregisterDelegateExtension(ExtensionPoint extensionPoint) {
- for (T extension : extensionPoint.getExtensions()) {
- if (extension instanceof CMakeNotificationProvider) {
- extensionPoint.unregisterExtension(extension);
- }
- }
- }
-}
diff --git a/sdkcompat/v231/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java b/sdkcompat/v231/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
deleted file mode 100644
index edb0594090b..00000000000
--- a/sdkcompat/v231/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.google.idea.sdkcompat.clion;
-
-
-import com.intellij.openapi.extensions.ExtensionPoint;
-import com.intellij.openapi.fileEditor.FileEditor;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.cidr.cpp.cmake.workspace.CMakeNotificationProvider;
-
-import java.util.function.Function;
-import javax.annotation.Nullable;
-import javax.swing.*;
-
-// #api223
-public class CMakeNotificationProviderWrapper {
- CMakeNotificationProvider value;
-
- public CMakeNotificationProviderWrapper(){
- this.value = new CMakeNotificationProvider();
- }
-
- @Nullable
- public JComponent createNotificationPanel(VirtualFile virtualFile, FileEditor fileEditor, Project project) {
- Function super FileEditor, ? extends JComponent> notificationProducer =
- this.value.collectNotificationData(project, virtualFile);
-
- if (notificationProducer != null) {
- return notificationProducer.apply(fileEditor);
- }
-
- return null;
- }
-
- public static void unregisterDelegateExtension(ExtensionPoint extensionPoint) {
- for (T extension : extensionPoint.getExtensions()) {
- if (extension instanceof CMakeNotificationProvider) {
- extensionPoint.unregisterExtension(extension);
- }
- }
- }
-}
diff --git a/sdkcompat/v232/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java b/sdkcompat/v232/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
deleted file mode 100644
index edb0594090b..00000000000
--- a/sdkcompat/v232/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.google.idea.sdkcompat.clion;
-
-
-import com.intellij.openapi.extensions.ExtensionPoint;
-import com.intellij.openapi.fileEditor.FileEditor;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.cidr.cpp.cmake.workspace.CMakeNotificationProvider;
-
-import java.util.function.Function;
-import javax.annotation.Nullable;
-import javax.swing.*;
-
-// #api223
-public class CMakeNotificationProviderWrapper {
- CMakeNotificationProvider value;
-
- public CMakeNotificationProviderWrapper(){
- this.value = new CMakeNotificationProvider();
- }
-
- @Nullable
- public JComponent createNotificationPanel(VirtualFile virtualFile, FileEditor fileEditor, Project project) {
- Function super FileEditor, ? extends JComponent> notificationProducer =
- this.value.collectNotificationData(project, virtualFile);
-
- if (notificationProducer != null) {
- return notificationProducer.apply(fileEditor);
- }
-
- return null;
- }
-
- public static void unregisterDelegateExtension(ExtensionPoint extensionPoint) {
- for (T extension : extensionPoint.getExtensions()) {
- if (extension instanceof CMakeNotificationProvider) {
- extensionPoint.unregisterExtension(extension);
- }
- }
- }
-}
diff --git a/sdkcompat/v233/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java b/sdkcompat/v233/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
deleted file mode 100644
index edb0594090b..00000000000
--- a/sdkcompat/v233/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.google.idea.sdkcompat.clion;
-
-
-import com.intellij.openapi.extensions.ExtensionPoint;
-import com.intellij.openapi.fileEditor.FileEditor;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.cidr.cpp.cmake.workspace.CMakeNotificationProvider;
-
-import java.util.function.Function;
-import javax.annotation.Nullable;
-import javax.swing.*;
-
-// #api223
-public class CMakeNotificationProviderWrapper {
- CMakeNotificationProvider value;
-
- public CMakeNotificationProviderWrapper(){
- this.value = new CMakeNotificationProvider();
- }
-
- @Nullable
- public JComponent createNotificationPanel(VirtualFile virtualFile, FileEditor fileEditor, Project project) {
- Function super FileEditor, ? extends JComponent> notificationProducer =
- this.value.collectNotificationData(project, virtualFile);
-
- if (notificationProducer != null) {
- return notificationProducer.apply(fileEditor);
- }
-
- return null;
- }
-
- public static void unregisterDelegateExtension(ExtensionPoint extensionPoint) {
- for (T extension : extensionPoint.getExtensions()) {
- if (extension instanceof CMakeNotificationProvider) {
- extensionPoint.unregisterExtension(extension);
- }
- }
- }
-}
diff --git a/sdkcompat/v241/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java b/sdkcompat/v241/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
deleted file mode 100644
index 46dfdbc93e3..00000000000
--- a/sdkcompat/v241/com/google/idea/sdkcompat/clion/CMakeNotificationProviderWrapper.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2023 The Bazel Authors. All rights reserved.
- *
- * 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.google.idea.sdkcompat.clion;
-
-
-import com.intellij.openapi.extensions.ExtensionPoint;
-import com.intellij.openapi.fileEditor.FileEditor;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.cidr.cpp.cmake.workspace.CMakeNotificationProvider;
-
-import java.util.function.Function;
-import javax.annotation.Nullable;
-import javax.swing.*;
-
-// #api223
-public class CMakeNotificationProviderWrapper {
- CMakeNotificationProvider value;
-
- public CMakeNotificationProviderWrapper(){
- this.value = new CMakeNotificationProvider();
- }
-
- @Nullable
- public JComponent createNotificationPanel(VirtualFile virtualFile, FileEditor fileEditor, Project project) {
- Function super FileEditor, ? extends JComponent> notificationProducer =
- this.value.collectNotificationData(project, virtualFile);
-
- if (notificationProducer != null) {
- return notificationProducer.apply(fileEditor);
- }
-
- return null;
- }
-
- public static void unregisterDelegateExtension(ExtensionPoint extensionPoint) {
- for (T extension : extensionPoint.getExtensions()) {
- if (extension instanceof CMakeNotificationProvider) {
- extensionPoint.unregisterExtension(extension);
- }
- }
- }
-}