Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.JavaCore;
Expand Down Expand Up @@ -106,7 +108,10 @@ private List<ResolutionItem> resolveMainClassUnderPaths(List<IPath> parentPaths)
projects = ProjectUtils.getJavaProjects();
} else {
projects = Stream.of(ProjectUtils.getAllProjects())
.filter(p -> ProjectUtils.isJavaProject(p) && p.getLocation() != null && ResourceUtils.isContainedIn(p.getLocation(), parentPaths))
.filter(p -> ProjectUtils.isJavaProject(p) && p.getLocation() != null
&& (ResourceUtils.isContainedIn(p.getLocation(), parentPaths)
|| isContainedInInvisibleProject(p, parentPaths)
|| hasSourceFolderContainedIn(p, parentPaths)))
.map(p -> JavaCore.create(p))
.filter(p -> p.exists())
.toArray(IJavaProject[]::new);
Expand Down Expand Up @@ -137,7 +142,8 @@ public void acceptSearchMatch(SearchMatch match) {
String projectName = ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) ? null : project.getName();
if (parentPaths.isEmpty()
|| ResourceUtils.isContainedIn(project.getLocation(), parentPaths)
|| isContainedInInvisibleProject(project, parentPaths)) {
|| isContainedInInvisibleProject(project, parentPaths)
|| isMatchContainedIn(match, parentPaths)) {
String filePath = null;

if (match.getResource() instanceof IFile) {
Expand Down Expand Up @@ -253,6 +259,55 @@ private boolean isMainMethod(IMethod method) {
return false;
}

/**
* Where the match itself is, for the projects the source folder check brings into the search:
* their source folders can be linked in from more than one workspace folder, and a folder is
* only asking about the main classes that are in it. The project checks stay in front of this
* one - a caller can ask with a project's own location rather than with a workspace folder, and
* for a project whose description lives outside of the workspace folders no match would be
* contained in that location. A match whose resource has no location falls back to them as well,
* since {@link ResourceUtils#isContainedIn} answers false for a null.
*/
private boolean isMatchContainedIn(SearchMatch match, Collection<IPath> rootPaths) {
IResource resource = match.getResource();
return resource != null && ResourceUtils.isContainedIn(resource.getLocation(), rootPaths);
}

/**
* A project can keep its description outside of the workspace folders and reach the sources
* through linked folders. The invisible project does it with a single link named after
* {@link ProjectUtils#WORKSPACE_LINK}, which the check below covers; project importers
* contributed by other extensions do it with one link per source root, which it does not. Such a
* project belongs to the folder its sources are in rather than to the folder its description
* happens to live in, so the source folders are checked as well - otherwise the project is
* dropped before the search runs and the folder appears to have no main class in it.
*/
private boolean hasSourceFolderContainedIn(IProject project, Collection<IPath> rootPaths) {
IJavaProject javaProject = JdtUtils.getJavaProject(project);
if (javaProject == null) {
return false;
}

try {
for (IClasspathEntry entry : javaProject.getRawClasspath()) {
if (entry.getEntryKind() != IClasspathEntry.CPE_SOURCE) {
continue;
}

IResource sourceFolder = ResourcesPlugin.getWorkspace().getRoot().findMember(entry.getPath());
if (sourceFolder != null && sourceFolder.getLocation() != null
&& ResourceUtils.isContainedIn(sourceFolder.getLocation(), rootPaths)) {
return true;
}
}
} catch (JavaModelException e) {
logger.log(Level.WARNING, String.format("Failed to read the classpath of project %s: %s",
project.getName(), e.toString()), e);
}

return false;
}

private boolean isContainedInInvisibleProject(IProject project, Collection<IPath> rootPaths) {
if (project == null) {
return false;
Expand Down