Skip to content
Draft
Show file tree
Hide file tree
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 @@ -126,6 +126,7 @@ public static List<PackageNode> resolvePath(List<Object> arguments, IProgressMon
throw new IllegalArgumentException("Should have one argument for resolvePath");
}
String typeRootUri = (String) arguments.get(0);
boolean mergeBuildOutputSourceRoots = arguments.size() > 1 && Boolean.TRUE.equals(arguments.get(1));
List<PackageNode> result = new ArrayList<>();
URI uri = JDTUtils.toURI(typeRootUri);
ITypeRoot typeRoot = ExtUtils.JDT_SCHEME.equals(uri.getScheme()) ? JDTUtils.resolveClassFile(uri) : JDTUtils.resolveCompilationUnit(uri);
Expand All @@ -143,12 +144,7 @@ public static List<PackageNode> resolvePath(List<Object> arguments, IProgressMon
if (isClassFile) {
result.add(PackageNode.createNodeForVirtualContainer(pkgRoot));
}
// for invisible project, removing the '_' link name may cause an empty named package root
// in this case, we will avoid that 'empty' node from displaying
PackageNode pkgRootNode = PackageNode.createNodeForPackageFragmentRoot(pkgRoot);
if (StringUtils.isNotBlank(pkgRootNode.getName())) {
result.add(pkgRootNode);
}
addPackageFragmentRootPath(result, pkgRoot, mergeBuildOutputSourceRoots);
if (!packageFragment.isDefaultPackage()) {
result.add(PackageNode.createNodeForPackageFragment(packageFragment));
}
Expand All @@ -158,7 +154,7 @@ public static List<PackageNode> resolvePath(List<Object> arguments, IProgressMon
IPackageFragmentRoot pkgRoot = resource.getPackageFragmentRoot();
result.add(PackageNode.createNodeForProject(pkgRoot));
result.add(PackageNode.createNodeForVirtualContainer(resource.getPackageFragmentRoot()));
result.add(PackageNode.createNodeForPackageFragmentRoot(pkgRoot));
addPackageFragmentRootPath(result, pkgRoot, mergeBuildOutputSourceRoots);
if (resource.getParent() instanceof IPackageFragment) {
IPackageFragment packageFragment = (IPackageFragment) resource.getParent();
if (!packageFragment.isDefaultPackage()) {
Expand Down Expand Up @@ -193,12 +189,7 @@ public static List<PackageNode> resolvePath(List<Object> arguments, IProgressMon
result.add(PackageNode.createNodeForProject(packageFragment));

IPackageFragmentRoot pkgRoot = (IPackageFragmentRoot) packageFragment.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
// for invisible project, removing the '_' link name may cause an empty named package root
// in this case, we will avoid that 'empty' node from displaying
PackageNode pkgRootNode = PackageNode.createNodeForPackageFragmentRoot(pkgRoot);
if (StringUtils.isNotBlank(pkgRootNode.getName())) {
result.add(pkgRootNode);
}
addPackageFragmentRootPath(result, pkgRoot, mergeBuildOutputSourceRoots);
if (!packageFragment.isDefaultPackage()) {
result.add(PackageNode.createNodeForPackageFragment(packageFragment));
}
Expand All @@ -207,7 +198,7 @@ public static List<PackageNode> resolvePath(List<Object> arguments, IProgressMon
item.setUri(JDTUtils.getFileURI(resource));
result.add(item);
} else {
return getParentAncestorNodes(resource);
return getParentAncestorNodes(resource, mergeBuildOutputSourceRoots);
}
} else {
IContainer container = JDTUtils.findFolder(typeRootUri);
Expand All @@ -227,18 +218,20 @@ public static List<PackageNode> resolvePath(List<Object> arguments, IProgressMon
* @return parent node list of element
* @throws JavaModelException when fails to get path or resource
*/
private static List<PackageNode> getParentAncestorNodes(IResource element) throws JavaModelException {
private static List<PackageNode> getParentAncestorNodes(IResource element,
boolean mergeBuildOutputSourceRoots) throws JavaModelException {
List<PackageNode> nodeList = new LinkedList<>();
while (element != null && !(element instanceof IWorkspaceRoot)) {
IJavaElement javaElement = JavaCore.create(element);
if (javaElement instanceof IJavaProject) {
nodeList.add(0, PackageNode.createNodeForProject(javaElement));
} else if (javaElement instanceof IPackageFragmentRoot) {
IPackageFragmentRoot pkgRoot = (IPackageFragmentRoot) javaElement;
nodeList.add(0, new PackageRootNode(pkgRoot,
element.getProjectRelativePath().toPortableString(), NodeKind.PACKAGEROOT));
nodeList.add(0, PackageNode.createNodeForProject(javaElement));
return nodeList;
List<PackageNode> prefix = new ArrayList<>();
prefix.add(PackageNode.createNodeForProject(javaElement));
addPackageFragmentRootPath(prefix, pkgRoot, mergeBuildOutputSourceRoots);
prefix.addAll(nodeList);
return prefix;
} else if (javaElement instanceof IPackageFragment) {
IPackageFragment packageFragment = (IPackageFragment) javaElement;
if (packageFragment.containsJavaResources() || packageFragment.getNonJavaResources().length > 0) {
Expand Down Expand Up @@ -303,6 +296,9 @@ private static List<PackageNode> getProjectChildren(PackageParams query, IProgre
ResourceVisitor visitor = new JavaResourceVisitor(javaProject);
resourceSet.accept(visitor);
List<PackageNode> result = visitor.getNodes();
if (query.isMergeBuildOutputSourceRoots()) {
mergeBuildOutputSourceRoots(result, javaProject);
}

// Invisible project will always have the referenced libraries entry
if (!ProjectUtils.isVisibleProject(project) || hasReferencedLibraries) {
Expand All @@ -311,6 +307,154 @@ private static List<PackageNode> getProjectChildren(PackageParams query, IProgre
return result;
}

private static void mergeBuildOutputSourceRoots(List<PackageNode> nodes, IJavaProject javaProject) {
if (javaProject == null) {
return;
}

try {
IFolder buildOutputRoot = getVisibleBuildOutputRoot(javaProject);
if (buildOutputRoot == null) {
return;
}

Set<IPath> sourceRootPaths = findSourceRootsUnder(javaProject, buildOutputRoot.getFullPath()).stream()
.map(IPackageFragmentRoot::getResource)
.filter(IFolder.class::isInstance)
.map(IResource::getFullPath)
.collect(Collectors.toSet());
nodes.removeIf(node -> node.getKind() == NodeKind.PACKAGEROOT
&& node.getPath() != null
&& sourceRootPaths.contains(Path.fromPortableString(node.getPath())));
} catch (JavaModelException e) {
JdtlsExtActivator.logException("Failed to merge generated source roots into the build output folder", e);
}
}

private static void addBuildOutputSourceRootChildren(List<PackageNode> nodes, IJavaProject javaProject,
IFolder folder) throws JavaModelException {
if (javaProject == null) {
return;
}

IFolder buildOutputRoot = getVisibleBuildOutputRoot(javaProject);
IPath folderPath = folder.getFullPath();
if (buildOutputRoot == null || !buildOutputRoot.getFullPath().isPrefixOf(folderPath)) {
return;
}

Set<IPath> existingPaths = nodes.stream()
.map(PackageNode::getPath)
.filter(StringUtils::isNotBlank)
.map(Path::fromPortableString)
.collect(Collectors.toSet());
for (IPackageFragmentRoot sourceRoot : findSourceRootsUnder(javaProject, folderPath)) {
IResource sourceResource = sourceRoot.getResource();
if (!(sourceResource instanceof IFolder)) {
continue;
}

IPath sourcePath = sourceResource.getFullPath();
if (folderPath.equals(sourcePath)) {
continue;
}

IPath relativePath = sourcePath.removeFirstSegments(folderPath.segmentCount());
IPath childPath = folderPath.append(relativePath.segment(0));
if (existingPaths.contains(childPath)) {
continue;
}

PackageNode childNode;
if (relativePath.segmentCount() == 1) {
PackageRootNode sourceRootNode = PackageNode.createNodeForPackageFragmentRoot(sourceRoot);
sourceRootNode.setDisplayName(relativePath.segment(0));
childNode = sourceRootNode;
} else {
IFolder childFolder = ResourcesPlugin.getWorkspace().getRoot().getFolder(childPath);
if (!childFolder.exists()) {
continue;
}
childNode = PackageNode.createNodeForFolder(childFolder);
}
nodes.add(childNode);
existingPaths.add(childPath);
}
}

private static void addPackageFragmentRootPath(List<PackageNode> result, IPackageFragmentRoot packageRoot,
boolean mergeBuildOutputSourceRoots) throws JavaModelException {
PackageRootNode packageRootNode = PackageNode.createNodeForPackageFragmentRoot(packageRoot);
// Removing the invisible project's '_' link may produce an empty package root.
if (StringUtils.isBlank(packageRootNode.getName())) {
return;
}

IResource packageRootResource = packageRoot.getResource();
IFolder buildOutputRoot = mergeBuildOutputSourceRoots
? getVisibleBuildOutputRoot(packageRoot.getJavaProject())
: null;
if (buildOutputRoot != null && packageRootResource instanceof IFolder
&& buildOutputRoot.getFullPath().isPrefixOf(packageRootResource.getFullPath())
&& !buildOutputRoot.getFullPath().equals(packageRootResource.getFullPath())) {
IPath packageRootPath = packageRootResource.getFullPath();
IPath currentPath = buildOutputRoot.getFullPath();
while (currentPath.segmentCount() < packageRootPath.segmentCount()) {
result.add(PackageNode.createNodeForFolder(
ResourcesPlugin.getWorkspace().getRoot().getFolder(currentPath)));
currentPath = currentPath.append(packageRootPath.segment(currentPath.segmentCount()));
}
packageRootNode.setDisplayName(packageRootResource.getName());
}
result.add(packageRootNode);
}

private static IFolder getVisibleBuildOutputRoot(IJavaProject javaProject) throws JavaModelException {
if (javaProject == null) {
return null;
}

IPath projectPath = javaProject.getPath();
IPath outputPath = javaProject.getOutputLocation();
if (!projectPath.isPrefixOf(outputPath) || outputPath.segmentCount() <= projectPath.segmentCount()) {
return null;
}

IPath buildOutputRootPath = projectPath.append(outputPath.segment(projectPath.segmentCount()));
for (Object resource : javaProject.getNonJavaResources()) {
if (!(resource instanceof IFolder)
|| !buildOutputRootPath.equals(((IFolder) resource).getFullPath())) {
continue;
}

List<Object> candidate = new ArrayList<>();
candidate.add(resource);
ResourceVisitor visitor = new JavaResourceVisitor(javaProject);
new ResourceSet(candidate).accept(visitor);
if (!visitor.getNodes().isEmpty()) {
return (IFolder) resource;
}
}
return null;
}

private static List<IPackageFragmentRoot> findSourceRootsUnder(IJavaProject javaProject, IPath ancestorPath)
throws JavaModelException {
List<IPackageFragmentRoot> result = new ArrayList<>();
for (IClasspathEntry entry : javaProject.getRawClasspath()) {
if (entry.getEntryKind() != IClasspathEntry.CPE_SOURCE) {
continue;
}
for (IPackageFragmentRoot packageRoot : javaProject.findPackageFragmentRoots(entry)) {
IResource resource = packageRoot.getResource();
if (resource instanceof IFolder && ancestorPath.isPrefixOf(resource.getFullPath())) {
result.add(packageRoot);
}
}
}
return result;
}

private static List<PackageNode> getContainerChildren(PackageParams query, IProgressMonitor pm) {
IJavaProject javaProject = getJavaProject(query.getProjectUri());
if (javaProject == null) {
Expand Down Expand Up @@ -447,6 +591,7 @@ public static List<Object> getChildrenForPackage(IPackageFragment packageFragmen
private static List<PackageNode> getFolderChildren(PackageParams query, IProgressMonitor pm) {
List<Object> children = new LinkedList<>();
IJavaProject javaProject = null;
IFolder folder = null;
try {
IPackageFragmentRoot packageRoot = getPackageFragmentRootFromQuery(query);
if (packageRoot != null) {
Expand All @@ -466,7 +611,7 @@ private static List<PackageNode> getFolderChildren(PackageParams query, IProgres
}
} else {
javaProject = packageRoot.getJavaProject();
IFolder folder = ResourcesPlugin.getWorkspace().getRoot().getFolder(Path.fromPortableString(query.getPath()));
folder = ResourcesPlugin.getWorkspace().getRoot().getFolder(Path.fromPortableString(query.getPath()));
if (folder.exists()) {
boolean isJavaElement = JavaCore.create(folder) != null;
children.addAll(Arrays.stream(folder.members())
Expand All @@ -477,7 +622,7 @@ private static List<PackageNode> getFolderChildren(PackageParams query, IProgres
}
} else {
// general resource folder.
IFolder folder = ResourcesPlugin.getWorkspace().getRoot().getFolder(Path.fromPortableString(query.getPath()));
folder = ResourcesPlugin.getWorkspace().getRoot().getFolder(Path.fromPortableString(query.getPath()));
if (folder.exists()) {
refreshLocal(folder, pm);
children.addAll(Arrays.asList(folder.members()));
Expand All @@ -488,7 +633,11 @@ private static List<PackageNode> getFolderChildren(PackageParams query, IProgres
ResourceSet resourceSet = new ResourceSet(children);
ResourceVisitor visitor = new JavaResourceVisitor(javaProject);
resourceSet.accept(visitor);
return visitor.getNodes();
List<PackageNode> result = visitor.getNodes();
if (query.isMergeBuildOutputSourceRoots() && folder != null) {
addBuildOutputSourceRootChildren(result, javaProject, folder);
}
return result;

} catch (CoreException e) {
JdtlsExtActivator.logException("Problem load project classfile list ", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public class PackageParams {

private boolean isHierarchicalView;

private boolean mergeBuildOutputSourceRoots;

/**
* Optional list of resource URIs (sent by the client on auto-refresh) that
* have just changed on disk. When present, the server only refreshes the
Expand All @@ -52,6 +54,14 @@ public void setHierarchicalView(boolean isHierarchicalView) {
this.isHierarchicalView = isHierarchicalView;
}

public boolean isMergeBuildOutputSourceRoots() {
return mergeBuildOutputSourceRoots;
}

public void setMergeBuildOutputSourceRoots(boolean mergeBuildOutputSourceRoots) {
this.mergeBuildOutputSourceRoots = mergeBuildOutputSourceRoots;
}

public PackageParams(NodeKind kind, String projectUri) {
this.kind = kind;
this.projectUri = projectUri;
Expand Down
11 changes: 9 additions & 2 deletions src/java/jdtls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ export namespace Jdtls {
export async function getPackageData(params: IPackageDataParam): Promise<INodeData[]> {
const uri: Uri | null = !params.projectUri ? null : Uri.parse(params.projectUri);
const excludePatterns: {[key: string]: boolean} | undefined = workspace.getConfiguration("files", uri).get("exclude");
const nonJavaResourcesFiltered: boolean = Settings.nonJavaResourcesFiltered();
params.mergeBuildOutputSourceRoots = !nonJavaResourcesFiltered;

let nodeData: INodeData[] = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND,
Commands.JAVA_GETPACKAGEDATA, params) || [];

// check filter settings.
if (Settings.nonJavaResourcesFiltered()) {
if (nonJavaResourcesFiltered) {
nodeData = nodeData.filter((data: INodeData) => {
return data.kind !== NodeKind.Folder && data.kind !== NodeKind.File;
});
Expand Down Expand Up @@ -65,7 +67,12 @@ export namespace Jdtls {
}

export async function resolvePath(params: string): Promise<INodeData[]> {
return await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_RESOLVEPATH, params) || [];
return await commands.executeCommand(
Commands.EXECUTE_WORKSPACE_COMMAND,
Commands.JAVA_RESOLVEPATH,
params,
!Settings.nonJavaResourcesFiltered(),
) || [];
}

export async function getMainClasses(params: string): Promise<IMainClassInfo[]> {
Expand Down
Loading