Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add commands to add/remove/list the project's source path #859

Merged
merged 1 commit into from
Dec 17, 2018
Merged
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
9 changes: 9 additions & 0 deletions org.eclipse.jdt.ls.core/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@
<command
id="java.project.resolveSourceAttachment">
</command>
<command
id="java.project.addToSourcePath">
</command>
<command
id="java.project.removeFromSourcePath">
</command>
<command
id="java.project.listSourcePaths">
</command>
</delegateCommandHandler>
</extension>
<extension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.ls.core.internal.commands.BuildPathCommand;
import org.eclipse.jdt.ls.core.internal.commands.OrganizeImportsCommand;
import org.eclipse.jdt.ls.core.internal.commands.SourceAttachmentCommand;
import org.eclipse.lsp4j.WorkspaceEdit;
Expand Down Expand Up @@ -45,6 +46,14 @@ public Object executeCommand(String commandId, List<Object> arguments, IProgress
return SourceAttachmentCommand.resolveSourceAttachment(arguments, monitor);
case "java.project.updateSourceAttachment":
return SourceAttachmentCommand.updateSourceAttachment(arguments, monitor);
case "java.project.addToSourcePath":
String sourceFolder = (String) arguments.get(0);
return BuildPathCommand.addToSourcePath(sourceFolder);
case "java.project.removeFromSourcePath":
String sourceFolder1 = (String) arguments.get(0);
return BuildPathCommand.removeFromSourcePath(sourceFolder1);
case "java.project.listSourcePaths":
return BuildPathCommand.listSourcePaths();
default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ public static boolean isGradleProject(IProject project) {
return hasNature(project, GradleProjectNature.ID);
}

public static boolean isGeneralJavaProject(IProject project) {
return isJavaProject(project) && !isMavenProject(project) && !isGradleProject(project);
}

public static String getJavaSourceLevel(IProject project) {
Map<String, String> options = getJavaOptions(project);
return options == null ? null : options.get(JavaCore.COMPILER_SOURCE);
Expand Down Expand Up @@ -136,15 +140,65 @@ public static boolean addSourcePath(IPath sourcePath, IPath[] exclusionPaths, IJ
return true;
}

public static boolean removeSourcePath(IPath sourcePath, IJavaProject project) throws JavaModelException {
IClasspathEntry[] existingEntries = project.getRawClasspath();
List<IClasspathEntry> newEntries = new ArrayList<>();
boolean found = false;
for (IClasspathEntry entry : existingEntries) {
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
if (entry.getPath().equals(sourcePath)) {
found = true;
} else {
newEntries.add(removeFilters(entry, sourcePath));
}
} else {
newEntries.add(entry);
}
}

if (found) {
project.setRawClasspath(newEntries.toArray(new IClasspathEntry[0]), project.getOutputLocation(), null);
return true;
}

return false;
}

public static IPath[] listSourcePaths(IJavaProject project) throws JavaModelException {
List<IPath> result = new ArrayList<>();
for (IClasspathEntry entry : project.getRawClasspath()) {
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
result.add(entry.getPath());
}
}

return result.toArray(new IPath[0]);
}

public static boolean isOnSourcePath(IPath sourcePath, IJavaProject project) throws JavaModelException {
for (IClasspathEntry entry : project.getRawClasspath()) {
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE && entry.getPath().equals(sourcePath)) {
return true;
}
}

return false;
}

public static IPath findBelongedWorkspaceRoot(IPath filePath) {
PreferenceManager manager = JavaLanguageServerPlugin.getPreferencesManager();
Collection<IPath> rootPaths = manager.getPreferences().getRootPaths();
if (rootPaths != null) {
for (IPath rootPath : rootPaths) {
if (rootPath.isPrefixOf(filePath)) {
return rootPath;
}
}
}

return null;
}

public static String getWorkspaceInvisibleProjectName(IPath workspacePath) {
String fileName = workspacePath.toFile().getName();
String projectName = fileName + "_" + Integer.toHexString(workspacePath.toPortableString().hashCode());
Expand Down Expand Up @@ -193,4 +247,32 @@ public static IProject createInvisibleProjectIfNotExist(IPath workspaceRoot) thr

return invisibleProject;
}

private static IClasspathEntry removeFilters(IClasspathEntry entry, IPath path) {
IPath[] inclusionPatterns = entry.getInclusionPatterns();
List<IPath> inclusionList = new ArrayList<>();
if (inclusionPatterns != null) {
for (IPath pattern : inclusionPatterns) {
if (!path.equals(entry.getPath().append(pattern))) {
inclusionList.add(pattern);
}
}
}

IPath[] exclusionPatterns = entry.getExclusionPatterns();
List<IPath> exclusionList = new ArrayList<>();
if (exclusionPatterns != null) {
for (IPath pattern : exclusionPatterns) {
if (!path.equals(entry.getPath().append(pattern))) {
exclusionList.add(pattern);
}
}
}

if ((inclusionPatterns == null || inclusionPatterns.length == inclusionList.size()) && (exclusionPatterns == null || exclusionPatterns.length == exclusionList.size())) {
return entry;
} else {
return JavaCore.newSourceEntry(entry.getPath(), inclusionList.toArray(new IPath[0]), exclusionList.toArray(new IPath[0]), entry.getOutputLocation(), entry.getExtraAttributes());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
/*******************************************************************************
* Copyright (c) 2018 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.ls.core.internal.commands;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.Messages;
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
import org.eclipse.jdt.ls.core.internal.ResourceUtils;
import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;

public class BuildPathCommand {
public static final String UNSUPPORTED_ON_MAVEN = "Unsupported operation. Please use pom.xml file to manage the source directories of maven project.";
public static final String UNSUPPORTED_ON_GRADLE = "Unsupported operation. Please use build.gradle file to manage the source directories of gradle project.";

public static Result addToSourcePath(String sourceFolderUri) {
IPath sourceFolderPath = ResourceUtils.filePathFromURI(sourceFolderUri);
IProject targetProject = findBelongedProject(sourceFolderPath);
if (targetProject != null && !ProjectUtils.isGeneralJavaProject(targetProject)) {
String message = ProjectUtils.isGradleProject(targetProject) ? UNSUPPORTED_ON_GRADLE : UNSUPPORTED_ON_MAVEN;
return new Result(false, message);
}

IPath projectLocation = null;
IContainer projectRootResource = null;
IPath[] exclusionPath = new IPath[0];
if (targetProject == null) {
try {
IPath workspaceRoot = ProjectUtils.findBelongedWorkspaceRoot(sourceFolderPath);
if (workspaceRoot == null) {
return new Result(false, Messages.format("The folder ''{0}'' doesn''t belong to any workspace.", getWorkspacePath(sourceFolderPath).toOSString()));
}

targetProject = ProjectUtils.createInvisibleProjectIfNotExist(workspaceRoot);
final IFolder workspaceLink = targetProject.getFolder(ProjectUtils.WORKSPACE_LINK);
projectLocation = workspaceRoot;
projectRootResource = workspaceLink;
List<IProject> subProjects = ProjectUtils.getVisibleProjects(workspaceRoot);
exclusionPath = subProjects.stream().map(project -> {
IPath relativePath = project.getLocation().makeRelativeTo(workspaceRoot);
return workspaceLink.getFolder(relativePath).getFullPath();
}).toArray(IPath[]::new);
} catch (OperationCanceledException | CoreException e) {
JavaLanguageServerPlugin.logException("Failed to create the invisible project.", e);
return new Result(false, "Failed to add the folder to the workspace invisible project's source path. Reason: " + e.getMessage());
}
} else {
projectLocation = targetProject.getLocation();
projectRootResource = targetProject;
}

IPath relativeSourcePath = sourceFolderPath.makeRelativeTo(projectLocation);
IPath sourcePath = relativeSourcePath.isEmpty() ? projectRootResource.getFullPath() : projectRootResource.getFolder(relativeSourcePath).getFullPath();
IJavaProject javaProject = JavaCore.create(targetProject);
try {
if (ProjectUtils.addSourcePath(sourcePath, exclusionPath, javaProject)) {
return new Result(true, Messages.format("Successfully added ''{0}'' to the project {1}''s source path.", new String[] { getWorkspacePath(sourceFolderPath).toOSString(), targetProject.getName() }));
} else {
return new Result(true, Messages.format("No need to add it to source path again, because the folder ''{0}'' is already in the project {1}''s source path.",
new String[] { getWorkspacePath(sourceFolderPath).toOSString(), targetProject.getName() }));
}
} catch (CoreException e) {
return new Result(false, e.getMessage());
}

}

public static Result removeFromSourcePath(String sourceFolderUri) {
IPath sourceFolderPath = ResourceUtils.filePathFromURI(sourceFolderUri);
IProject targetProject = findBelongedProject(sourceFolderPath);
if (targetProject != null && !ProjectUtils.isGeneralJavaProject(targetProject)) {
String message = ProjectUtils.isGradleProject(targetProject) ? UNSUPPORTED_ON_GRADLE : UNSUPPORTED_ON_MAVEN;
return new Result(false, message);
}

IPath projectLocation = null;
IContainer projectRootResource = null;
if (targetProject == null) {
IPath workspaceRoot = ProjectUtils.findBelongedWorkspaceRoot(sourceFolderPath);
if (workspaceRoot == null) {
return new Result(false, Messages.format("The folder ''{0}'' doesn''t belong to any workspace.", getWorkspacePath(sourceFolderPath).toOSString()));
}

String invisibleProjectName = ProjectUtils.getWorkspaceInvisibleProjectName(workspaceRoot);
targetProject = ResourcesPlugin.getWorkspace().getRoot().getProject(invisibleProjectName);
if (!targetProject.exists()) {
return new Result(true, Messages.format("No need to remove it from source path, because the folder ''{0}'' isn''t on any project''s source path.", getWorkspacePath(sourceFolderPath).toOSString()));
}

projectLocation = workspaceRoot;
projectRootResource = targetProject.getFolder(ProjectUtils.WORKSPACE_LINK);
} else {
projectLocation = targetProject.getLocation();
projectRootResource = targetProject;
}

IPath relativeSourcePath = sourceFolderPath.makeRelativeTo(projectLocation);
IPath sourcePath = relativeSourcePath.isEmpty() ? projectRootResource.getFullPath() : projectRootResource.getFolder(relativeSourcePath).getFullPath();
IJavaProject javaProject = JavaCore.create(targetProject);
try {
if (ProjectUtils.removeSourcePath(sourcePath, javaProject)) {
return new Result(true, Messages.format("Successfully removed ''{0}'' from the project {1}''s source path.", new String[] { getWorkspacePath(sourceFolderPath).toOSString(), targetProject.getName() }));
} else {
return new Result(true, Messages.format("No need to remove it from source path, because the folder ''{0}'' isn''t on any project''s source path.", getWorkspacePath(sourceFolderPath).toOSString()));
}
} catch (CoreException e) {
return new Result(false, e.getMessage());
}
}

public static Result listSourcePaths() {
List<SourcePath> sourcePathList = new ArrayList<>();
IProject[] projects = ProjectUtils.getAllProjects();
for (IProject project : projects) {
if (!ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) && ProjectUtils.isJavaProject(project)) {
try {
IPath[] paths = ProjectUtils.listSourcePaths(JavaCore.create(project));
for (IPath path : paths) {
IPath entryPath = path;
String projectName = project.getName();
String projectType = "General";
if (ProjectUtils.isMavenProject(project)) {
projectType = "Maven";
}

if (ProjectUtils.isGradleProject(project)) {
projectType = "Gradle";
}

IContainer projectRoot = project;
if (!ProjectUtils.isVisibleProject(project)) {
projectType = "Workspace";
IFolder workspaceLinkFolder = project.getFolder(ProjectUtils.WORKSPACE_LINK);
if (!workspaceLinkFolder.isLinked()) {
continue;
}

projectRoot = workspaceLinkFolder;
}

IPath relativePath = entryPath.makeRelativeTo(projectRoot.getFullPath());
IPath location = projectRoot.getRawLocation().append(relativePath);
IPath displayPath = getWorkspacePath(location);
sourcePathList.add(new SourcePath(location != null ? location.toOSString() : "", displayPath != null ? displayPath.toOSString() : entryPath.toOSString(), projectName, projectType));
}
} catch (JavaModelException e) {
JavaLanguageServerPlugin.logException("Failed to resolve the existing source paths in current workspace.", e);
return new ListCommandResult(false, e.getMessage());
}
}
}

return new ListCommandResult(true, null, sourcePathList.toArray(new SourcePath[0]));
}

private static IProject findBelongedProject(IPath sourceFolder) {
List<IProject> projects = Stream.of(ProjectUtils.getAllProjects()).filter(ProjectUtils::isJavaProject).sorted(new Comparator<IProject>() {
@Override
public int compare(IProject p1, IProject p2) {
return p2.getLocation().toOSString().length() - p1.getLocation().toOSString().length();
}
}).collect(Collectors.toList());

for (IProject project : projects) {
if (project.getLocation().isPrefixOf(sourceFolder)) {
return project;
}
}

return null;
}

private static IPath getWorkspacePath(IPath path) {
PreferenceManager manager = JavaLanguageServerPlugin.getPreferencesManager();
Collection<IPath> rootPaths = manager.getPreferences().getRootPaths();
if (rootPaths != null) {
for (IPath rootPath : rootPaths) {
if (rootPath.isPrefixOf(path)) {
return path.makeRelativeTo(rootPath.append(".."));
}
}
}

return path;
}

public static class Result {
public boolean status;
public String message;

Result(boolean status, String message) {
this.status = status;
this.message = message;
}
}

public static class ListCommandResult extends Result {
public SourcePath[] data;

ListCommandResult(boolean status, String message) {
super(status, message);
data = new SourcePath[0];
}

ListCommandResult(boolean status, String message, SourcePath[] data) {
super(status, message);
this.data = data;
}
}

public static class SourcePath {
public String path;
public String displayPath;
public String projectName;
public String projectType;

SourcePath(String path, String displayPath, String projectName, String projectType) {
this.path = path;
this.displayPath = displayPath;
this.projectName = projectName;
this.projectType = projectType;
}
}
}
Loading