diff --git a/protobuf/protoeditor-core/src/com/intellij/protobuf/lang/resolve/AdditionalLibrariesFileResolveProvider.java b/protobuf/protoeditor-core/src/com/intellij/protobuf/lang/resolve/AdditionalLibrariesFileResolveProvider.java new file mode 100644 index 00000000000..b020ee7a6f3 --- /dev/null +++ b/protobuf/protoeditor-core/src/com/intellij/protobuf/lang/resolve/AdditionalLibrariesFileResolveProvider.java @@ -0,0 +1,76 @@ +package com.intellij.protobuf.lang.resolve; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.AdditionalLibraryRootsProvider; +import com.intellij.openapi.roots.SyntheticLibrary; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.GlobalSearchScopesCore; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class AdditionalLibrariesFileResolveProvider implements FileResolveProvider { + @NotNull + @Override + public Collection getChildEntries(@NotNull String path, @NotNull Project project) { + VirtualFile pathFile = findFile(path, project); + if (pathFile == null || !pathFile.isDirectory()) { + return Collections.emptyList(); + } + VirtualFile[] children = pathFile.getChildren(); + if (children == null) { + return Collections.emptyList(); + } + + Set results = new HashSet<>(); + + for (VirtualFile child : children) { + if (PROTO_AND_DIRECTORY_FILTER.accept(child)) { + results.add(new ChildEntry(child.getName(), child.isDirectory())); + } + } + return results; + } + + @Nullable + @Override + public VirtualFile findFile(@NotNull String path, @NotNull Project project) { + for (AdditionalLibraryRootsProvider provider : AdditionalLibraryRootsProvider.EP_NAME.getExtensionList()) { + for (SyntheticLibrary library : provider.getAdditionalProjectLibraries(project)) { + for (VirtualFile sourceRoot : library.getSourceRoots()) { + if (sourceRoot.isDirectory()) { + VirtualFile pbFile = sourceRoot.findFileByRelativePath(path); + if (pbFile != null) { + return pbFile; + } + } + if (sourceRoot.getPath().endsWith(path)) { + return sourceRoot; + } + } + } + } + return null; + } + + @Nullable + @Override + public VirtualFile getDescriptorFile(@NotNull Project project) { + return null; + } + + @NotNull + @Override + public GlobalSearchScope getSearchScope(@NotNull Project project) { + VirtualFile[] roots = AdditionalLibraryRootsProvider.EP_NAME.getExtensionList().stream() + .map(provider -> provider.getAdditionalProjectLibraries(project)) + .flatMap(Collection::stream) + .map(SyntheticLibrary::getSourceRoots) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .toArray(VirtualFile[]::new); + return GlobalSearchScopesCore.directoriesScope(project, /* withSubDirectories= */ true, roots); + } +} diff --git a/protobuf/protoeditor-core/test/com/intellij/protobuf/lang/resolve/AdditionalLibrariesFileResolveProviderTest.java b/protobuf/protoeditor-core/test/com/intellij/protobuf/lang/resolve/AdditionalLibrariesFileResolveProviderTest.java new file mode 100644 index 00000000000..127e8644805 --- /dev/null +++ b/protobuf/protoeditor-core/test/com/intellij/protobuf/lang/resolve/AdditionalLibrariesFileResolveProviderTest.java @@ -0,0 +1,88 @@ +package com.intellij.protobuf.lang.resolve; + +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.AdditionalLibraryRootsProvider; +import com.intellij.openapi.roots.SyntheticLibrary; +import com.intellij.openapi.roots.ex.ProjectRootManagerEx; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.protobuf.fixtures.PbCodeInsightFixtureTestCase; +import com.intellij.protobuf.lang.resolve.FileResolveProvider.ChildEntry; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * Unit tests for {@link AdditionalLibrariesFileResolveProvider}. + */ +public class AdditionalLibrariesFileResolveProviderTest extends PbCodeInsightFixtureTestCase { + + private File tempDir = null; + + @Override + public void setUp() throws Exception { + super.setUp(); + tempDir = FileUtil.createTempDirectory(getName(), UUID.randomUUID().toString(), false); + WriteAction.run(() -> ProjectRootManagerEx.getInstanceEx(getProject()).makeRootsChange( + () -> AdditionalLibraryRootsProvider.EP_NAME.getPoint().registerExtension(new AdditionalLibraryRootsProvider() { + @NotNull + @Override + public Collection getAdditionalProjectLibraries(@NotNull Project project) { + List roots = Collections.singletonList(VfsUtil.findFile(tempDir.toPath(), true)); + return Collections.singletonList(SyntheticLibrary.newImmutableLibrary(roots)); + } + }, getTestRootDisposable()), false, true)); + } + + @Override + public void tearDown() throws Exception { + FileUtil.delete(tempDir); + super.tearDown(); + } + + public void testFindFilePrefersFirstListedPath() throws Exception { + FileUtil.writeToFile(new File(tempDir, "com/foo/dir/foo.proto"), "// foo"); + + VirtualFileManager.getInstance().syncRefresh(); + + FileResolveProvider resolver = new AdditionalLibrariesFileResolveProvider(); + VirtualFile foo = resolver.findFile("com/foo/dir/foo.proto", getProject()); + + assertNotNull(foo); + + assertEquals("// foo", VfsUtil.loadText(foo)); + } + + public void testGetChildEntries() throws Exception { + FileUtil.writeToFile(new File(tempDir, "com/foo/dir/foo.proto"), "// foo"); + FileUtil.writeToFile(new File(tempDir, "com/foo/dir/bar.proto"), "// bar"); + + VirtualFileManager.getInstance().syncRefresh(); + + FileResolveProvider resolver = new AdditionalLibrariesFileResolveProvider(); + assertContainsElements( + resolver.getChildEntries("", getProject()), + ChildEntry.directory("com")); + assertContainsElements( + resolver.getChildEntries("com", getProject()), ChildEntry.directory("foo")); + assertContainsElements( + resolver.getChildEntries("com/", getProject()), ChildEntry.directory("foo")); + assertContainsElements( + resolver.getChildEntries("com/foo", getProject()), ChildEntry.directory("dir")); + assertContainsElements( + resolver.getChildEntries("com/foo/dir", getProject()), + ChildEntry.file("foo.proto"), + ChildEntry.file("bar.proto")); + assertContainsElements( + resolver.getChildEntries("com/foo/dir/", getProject()), + ChildEntry.file("foo.proto"), + ChildEntry.file("bar.proto")); + } +} diff --git a/protobuf/resources/META-INF/plugin.xml b/protobuf/resources/META-INF/plugin.xml index 4a7fbb40172..609e13bb678 100644 --- a/protobuf/resources/META-INF/plugin.xml +++ b/protobuf/resources/META-INF/plugin.xml @@ -53,6 +53,7 @@ +