From 684f8982a3a6edb5d8c7b50c946aa8b0cd943e10 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 29 Mar 2012 16:17:54 +0200 Subject: [PATCH] prefer current library when navigating from its source --- .../psi/resolve/ResolveInLibrariesTest.groovy | 54 +++++++++++++++++++ .../scopes/LibraryRuntimeClasspathScope.java | 13 +++-- .../file/impl/ResolveScopeManagerImpl.java | 27 +++++++++- .../intellij/testFramework/PsiTestUtil.java | 27 ++++++++-- .../EclipseLibrariesModificationsTest.java | 19 ++----- 5 files changed, 114 insertions(+), 26 deletions(-) create mode 100644 java/java-tests/testSrc/com/intellij/psi/resolve/ResolveInLibrariesTest.groovy diff --git a/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveInLibrariesTest.groovy b/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveInLibrariesTest.groovy new file mode 100644 index 000000000000..ceb2bbd93df6 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveInLibrariesTest.groovy @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2012 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. + */ +package com.intellij.psi.resolve + +import com.intellij.openapi.application.ex.PathManagerEx +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.testFramework.PsiTestUtil +import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase + +/** + * @author peter + */ +class ResolveInLibrariesTest extends JavaCodeInsightFixtureTestCase { + + public void "test prefer current library when navigation from its source"() { + def lib = LocalFileSystem.getInstance().refreshAndFindFileByPath(PathManagerEx.getTestDataPath() + "/../../../lib") + def nanoJar = lib.children.find { it.name.startsWith("nanoxml") } + def nanoSrc = lib.findChild("src").children.find { it.name.startsWith("nanoxml") } + + def jarCopy = myFixture.copyFileToProject(nanoJar.path, 'lib/nanoJar.jar') + def srcCopy = myFixture.copyFileToProject(nanoSrc.path, 'lib/nanoSrc.zip') + + PsiTestUtil.addLibrary(myModule, 'nano1', lib.path, ["/$nanoJar.name!/"] as String[], ["/src/$nanoSrc.name!/"] as String[]) + PsiTestUtil.addLibrary(myModule, 'nano2', jarCopy.parent.path, ["/$jarCopy.name!/"] as String[], ["/$srcCopy.name!/"] as String[]) + + def parsers = JavaPsiFacade.getInstance(project).findClasses('net.n3.nanoxml.IXMLParser', GlobalSearchScope.allScope(project)) + assert parsers.size() == 2 + + def file0 = parsers[0].navigationElement.containingFile + assert file0.virtualFile.path.startsWith(nanoSrc.path) + assert file0.findReferenceAt(file0.text.indexOf('IXMLReader reader')).resolve().navigationElement.containingFile.virtualFile.path.startsWith(nanoSrc.path) + + def file1 = parsers[1].navigationElement.containingFile + assert file1.virtualFile.path.startsWith(srcCopy.path) + assert file1.findReferenceAt(file1.text.indexOf('IXMLReader reader')).resolve().navigationElement.containingFile.virtualFile.path.startsWith(srcCopy.path) + + } + +} diff --git a/platform/lang-impl/src/com/intellij/openapi/module/impl/scopes/LibraryRuntimeClasspathScope.java b/platform/lang-impl/src/com/intellij/openapi/module/impl/scopes/LibraryRuntimeClasspathScope.java index dbbbacc90f1d..90ad86b3980b 100644 --- a/platform/lang-impl/src/com/intellij/openapi/module/impl/scopes/LibraryRuntimeClasspathScope.java +++ b/platform/lang-impl/src/com/intellij/openapi/module/impl/scopes/LibraryRuntimeClasspathScope.java @@ -29,6 +29,7 @@ import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -39,11 +40,9 @@ import java.util.Set; public class LibraryRuntimeClasspathScope extends GlobalSearchScope { private final ProjectFileIndex myIndex; private final LinkedHashSet myEntries = new LinkedHashSet(); - private final List myModules; public LibraryRuntimeClasspathScope(final Project project, final List modules) { super(project); - myModules = modules; myIndex = ProjectRootManager.getInstance(project).getFileIndex(); final Set processedSdk = new THashSet(); final Set processedLibraries = new THashSet(); @@ -63,8 +62,14 @@ public class LibraryRuntimeClasspathScope extends GlobalSearchScope { } } + public LibraryRuntimeClasspathScope(Project project, LibraryOrderEntry entry) { + super(project); + myIndex = ProjectRootManager.getInstance(project).getFileIndex(); + Collections.addAll(myEntries, entry.getRootFiles(OrderRootType.CLASSES)); + } + public int hashCode() { - return myModules.hashCode(); + return myEntries.hashCode(); } public boolean equals(Object object) { @@ -72,7 +77,7 @@ public class LibraryRuntimeClasspathScope extends GlobalSearchScope { if (object == null || object.getClass() != LibraryRuntimeClasspathScope.class) return false; final LibraryRuntimeClasspathScope that = (LibraryRuntimeClasspathScope)object; - return that.myModules.equals(myModules); + return that.myEntries.equals(myEntries); } private void buildEntries(@NotNull final Module module, diff --git a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/ResolveScopeManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/ResolveScopeManagerImpl.java index d00bba8e12d7..a7c4277861f2 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/ResolveScopeManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/ResolveScopeManagerImpl.java @@ -16,6 +16,7 @@ package com.intellij.psi.impl.file.impl; import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.impl.scopes.LibraryRuntimeClasspathScope; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; @@ -25,6 +26,7 @@ import com.intellij.psi.*; import com.intellij.psi.impl.PsiManagerImpl; import com.intellij.psi.impl.ResolveScopeManager; import com.intellij.psi.impl.source.resolve.FileContextUtil; +import com.intellij.psi.search.DelegatingGlobalSearchScope; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.SearchScope; import com.intellij.util.containers.ConcurrentFactoryMap; @@ -91,6 +93,7 @@ public class ResolveScopeManagerImpl extends ResolveScopeManager { List modulesLibraryUsedIn = new ArrayList(); List orderEntries = projectFileIndex.getOrderEntriesForFile(vFile); + LibraryOrderEntry lib = null; for (OrderEntry entry : orderEntries) { ProgressManager.checkCanceled(); @@ -98,12 +101,32 @@ public class ResolveScopeManagerImpl extends ResolveScopeManager { return ((ProjectRootManagerEx)myProjectRootManager).getScopeForJdk((JdkOrderEntry)entry); } - if (entry instanceof LibraryOrderEntry || entry instanceof ModuleOrderEntry) { + if (entry instanceof LibraryOrderEntry) { + lib = (LibraryOrderEntry)entry; + modulesLibraryUsedIn.add(entry.getOwnerModule()); + } + else if (entry instanceof ModuleOrderEntry) { modulesLibraryUsedIn.add(entry.getOwnerModule()); } } - return ((ProjectRootManagerEx)myProjectRootManager).getScopeForLibraryUsedIn(modulesLibraryUsedIn); + GlobalSearchScope allCandidates = ((ProjectRootManagerEx)myProjectRootManager).getScopeForLibraryUsedIn(modulesLibraryUsedIn); + if (lib != null) { + final LibraryRuntimeClasspathScope preferred = new LibraryRuntimeClasspathScope(myProject, lib); + // prefer current library + return new DelegatingGlobalSearchScope(allCandidates, preferred) { + @Override + public int compare(VirtualFile file1, VirtualFile file2) { + boolean c1 = preferred.contains(file1); + boolean c2 = preferred.contains(file2); + if (c1 && !c2) return 1; + if (c2 && !c1) return -1; + + return super.compare(file1, file2); + } + }; + } + return allCandidates; } } diff --git a/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java index 480dc6312069..4f66a034eae0 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java @@ -30,10 +30,7 @@ import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.JarFileSystem; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.*; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; @@ -278,4 +275,26 @@ import java.util.Collection; } }.execute().throwException(); } + + public static void addLibrary(final Module module, + final String libName, final String libDir, + final String[] classRoots, + final String[] sourceRoots) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + final ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel(); + final String parentUrl = VirtualFileManager.constructUrl(JarFileSystem.PROTOCOL, libDir); + final Library library = model.getModuleLibraryTable().createLibrary(libName); + final Library.ModifiableModel libModifiableModel = library.getModifiableModel(); + for (String classRoot : classRoots) { + libModifiableModel.addRoot(parentUrl + classRoot, OrderRootType.CLASSES); + } + for (String sourceRoot : sourceRoots) { + libModifiableModel.addRoot(parentUrl + sourceRoot, OrderRootType.SOURCES); + } + libModifiableModel.commit(); + model.commit(); + } + }); + } } \ No newline at end of file diff --git a/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseLibrariesModificationsTest.java b/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseLibrariesModificationsTest.java index 05dfc5829a4b..d7972ddbb3fc 100644 --- a/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseLibrariesModificationsTest.java +++ b/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseLibrariesModificationsTest.java @@ -30,6 +30,7 @@ import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.testFramework.PsiTestUtil; import com.intellij.util.ArrayUtil; public class EclipseLibrariesModificationsTest extends EclipseVarsTest { @@ -38,22 +39,8 @@ public class EclipseLibrariesModificationsTest extends EclipseVarsTest { final Project project = getProject(); final String path = project.getBaseDir().getPath() + "/test"; final Module module = EclipseClasspathTest.setUpModule(path, project); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - public void run() { - final ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel(); - final String parentUrl = VirtualFileManager.constructUrl(JarFileSystem.PROTOCOL, model.getContentRoots()[0].getParent().getPath()); - final Library library = model.getModuleLibraryTable().createLibrary("created"); - final Library.ModifiableModel libModifiableModel = library.getModifiableModel(); - for (String classRoot : classRoots) { - libModifiableModel.addRoot(parentUrl + classRoot, OrderRootType.CLASSES); - } - for (String sourceRoot : sourceRoots) { - libModifiableModel.addRoot(parentUrl + sourceRoot, OrderRootType.SOURCES); - } - libModifiableModel.commit(); - model.commit(); - } - }); + PsiTestUtil.addLibrary(module, "created", ModuleRootManager.getInstance(module).getContentRoots()[0].getParent().getPath(), classRoots, + sourceRoots); EclipseClasspathTest.checkModule(project.getBaseDir().getPath() + "/expected", module); }